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

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

Issue 10909151: Automatically fall back to non-low latency on open() failure. (Closed) Base URL: http://git.chromium.org/chromium/src.git@resampler2
Patch Set: Rebase. 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
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/compiler_specific.h" 9 #include "base/compiler_specific.h"
10 #include "base/message_loop.h" 10 #include "base/message_loop.h"
11 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram.h"
12 #include "base/stringprintf.h"
12 #include "base/time.h" 13 #include "base/time.h"
13 #include "media/audio/audio_io.h" 14 #include "media/audio/audio_io.h"
14 #include "media/audio/audio_output_dispatcher_impl.h" 15 #include "media/audio/audio_output_dispatcher_impl.h"
15 #include "media/audio/audio_output_proxy.h" 16 #include "media/audio/audio_output_proxy.h"
16 #include "media/audio/audio_util.h" 17 #include "media/audio/audio_util.h"
17 #include "media/audio/sample_rates.h" 18 #include "media/audio/sample_rates.h"
18 #include "media/base/audio_pull_fifo.h" 19 #include "media/base/audio_pull_fifo.h"
19 #include "media/base/limits.h" 20 #include "media/base/limits.h"
20 #include "media/base/multi_channel_resampler.h" 21 #include "media/base/multi_channel_resampler.h"
21 22
22 #if defined(OS_WIN) 23 #if defined(OS_WIN)
23 #include "media/audio/win/audio_low_latency_output_win.h" 24 #include "media/audio/win/audio_low_latency_output_win.h"
24 #endif 25 #endif
25 26
26 namespace media { 27 namespace media {
27 28
28 static void RecordStats(const AudioParameters& output_params) { 29 // Record UMA statistics for hardware output configuration. |prefix| will be
30 // prepended to the statistic name.
31 static void RecordStats(const char* prefix,
32 const AudioParameters& output_params) {
29 UMA_HISTOGRAM_ENUMERATION( 33 UMA_HISTOGRAM_ENUMERATION(
30 "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(), 34 base::StringPrintf("Media.%sHardwareAudioBitsPerChannel", prefix),
31 limits::kMaxBitsPerSample); 35 output_params.bits_per_sample(), limits::kMaxBitsPerSample);
32 #if defined(OS_WIN) 36 #if defined(OS_WIN)
33 // TODO(dalecurtis): Since channel mixing is handle by the output device right 37 // TODO(dalecurtis): Since channel mixing is handle by the output device right
34 // now and not by AudioOutputResampler, we need to query for hardware channel 38 // now and not by AudioOutputResampler, we need to query for hardware channel
35 // information. Remove once AOR handles this, http://crbug.com/138762 39 // information. Remove once AOR handles this, http://crbug.com/138762
36 UMA_HISTOGRAM_ENUMERATION( 40 UMA_HISTOGRAM_ENUMERATION(
37 "Media.HardwareAudioChannelLayout", 41 base::StringPrintf("Media.%sHardwareAudioChannelLayout", prefix),
38 WASAPIAudioOutputStream::HardwareChannelLayout(), CHANNEL_LAYOUT_MAX); 42 WASAPIAudioOutputStream::HardwareChannelLayout(), CHANNEL_LAYOUT_MAX);
39 UMA_HISTOGRAM_ENUMERATION( 43 UMA_HISTOGRAM_ENUMERATION(
40 "Media.HardwareAudioChannelCount", 44 base::StringPrintf("Media.%sHardwareAudioChannelCount", prefix),
41 WASAPIAudioOutputStream::HardwareChannelCount(), limits::kMaxChannels); 45 WASAPIAudioOutputStream::HardwareChannelCount(), limits::kMaxChannels);
42 #else 46 #else
43 UMA_HISTOGRAM_ENUMERATION( 47 UMA_HISTOGRAM_ENUMERATION(
44 "Media.HardwareAudioChannelLayout", output_params.channel_layout(), 48 base::StringPrintf("Media.%sHardwareAudioChannelLayout", prefix),
45 CHANNEL_LAYOUT_MAX); 49 output_params.channel_layout(), CHANNEL_LAYOUT_MAX);
46 UMA_HISTOGRAM_ENUMERATION( 50 UMA_HISTOGRAM_ENUMERATION(
47 "Media.HardwareAudioChannelCount", output_params.channels(), 51 base::StringPrintf("Media.%sHardwareAudioChannelCount", prefix),
48 limits::kMaxChannels); 52 output_params.channels(), limits::kMaxChannels);
49 #endif 53 #endif
50 AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate()); 54 AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate());
51 if (asr != kUnexpectedAudioSampleRate) { 55 if (asr != kUnexpectedAudioSampleRate) {
52 UMA_HISTOGRAM_ENUMERATION( 56 UMA_HISTOGRAM_ENUMERATION(
53 "Media.HardwareAudioSamplesPerSecond", asr, kUnexpectedAudioSampleRate); 57 base::StringPrintf("Media.%sHardwareAudioSamplesPerSecond", prefix),
58 asr, kUnexpectedAudioSampleRate);
54 } else { 59 } else {
55 UMA_HISTOGRAM_COUNTS( 60 UMA_HISTOGRAM_COUNTS(
56 "Media.HardwareAudioSamplesPerSecondUnexpected", 61 base::StringPrintf("Media.%sHardwareAudioSamplesPerSecondUnexpected",
57 output_params.sample_rate()); 62 prefix), output_params.sample_rate());
58 } 63 }
59 } 64 }
60 65
61 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, 66 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
62 const AudioParameters& input_params, 67 const AudioParameters& input_params,
63 const AudioParameters& output_params, 68 const AudioParameters& output_params,
64 const base::TimeDelta& close_delay) 69 const base::TimeDelta& close_delay)
65 : AudioOutputDispatcher(audio_manager, input_params), 70 : AudioOutputDispatcher(audio_manager, input_params),
66 source_callback_(NULL), 71 source_callback_(NULL),
67 io_ratio_(1), 72 io_ratio_(1),
68 input_bytes_per_frame_(input_params.GetBytesPerFrame()), 73 close_delay_(close_delay),
69 output_bytes_per_frame_(output_params.GetBytesPerFrame()), 74 outstanding_audio_bytes_(0),
70 outstanding_audio_bytes_(0) { 75 output_params_(output_params) {
76 Initialize(true);
scherkus (not reviewing) 2012/09/12 13:45:15 instead of passing true to call RecordStats ... ho
DaleCurtis 2012/09/12 14:16:51 Done.
77 }
78
79 AudioOutputResampler::~AudioOutputResampler() {}
80
81 void AudioOutputResampler::Initialize(bool record_stats) {
82 io_ratio_ = 1;
83
71 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762 84 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762
72 DCHECK_EQ(input_params.channels(), output_params.channels()); 85 DCHECK_EQ(params_.channels(), output_params_.channels());
73 // Only resample or rebuffer if the input parameters don't match the output 86 // Only resample or rebuffer if the input parameters don't match the output
74 // parameters to avoid any unnecessary work. 87 // parameters to avoid any unnecessary work.
75 if (input_params.channels() != output_params.channels() || 88 if (params_.channels() != output_params_.channels() ||
76 input_params.sample_rate() != output_params.sample_rate() || 89 params_.sample_rate() != output_params_.sample_rate() ||
77 input_params.bits_per_sample() != output_params.bits_per_sample() || 90 params_.bits_per_sample() != output_params_.bits_per_sample() ||
78 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { 91 params_.frames_per_buffer() != output_params_.frames_per_buffer()) {
79 // Only resample if necessary since it's expensive. 92 // Only resample if necessary since it's expensive.
80 if (input_params.sample_rate() != output_params.sample_rate()) { 93 if (params_.sample_rate() != output_params_.sample_rate()) {
81 DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to " 94 DVLOG(1) << "Resampling from " << params_.sample_rate() << " to "
82 << output_params.sample_rate(); 95 << output_params_.sample_rate();
83 double io_sample_rate_ratio = input_params.sample_rate() / 96 double io_sample_rate_ratio = params_.sample_rate() /
84 static_cast<double>(output_params.sample_rate()); 97 static_cast<double>(output_params_.sample_rate());
85 // Include the I/O resampling ratio in our global I/O ratio. 98 // Include the I/O resampling ratio in our global I/O ratio.
86 io_ratio_ *= io_sample_rate_ratio; 99 io_ratio_ *= io_sample_rate_ratio;
87 resampler_.reset(new MultiChannelResampler( 100 resampler_.reset(new MultiChannelResampler(
88 output_params.channels(), io_sample_rate_ratio, base::Bind( 101 output_params_.channels(), io_sample_rate_ratio, base::Bind(
89 &AudioOutputResampler::ProvideInput, base::Unretained(this)))); 102 &AudioOutputResampler::ProvideInput, base::Unretained(this))));
90 } 103 }
91 104
92 // Include bits per channel differences. 105 // Include bits per channel differences.
93 io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) / 106 io_ratio_ *= static_cast<double>(params_.bits_per_sample()) /
94 output_params.bits_per_sample(); 107 output_params_.bits_per_sample();
95 108
96 // Include channel count differences. 109 // Include channel count differences.
97 io_ratio_ *= static_cast<double>(input_params.channels()) / 110 io_ratio_ *= static_cast<double>(params_.channels()) /
98 output_params.channels(); 111 output_params_.channels();
99 112
100 // Since the resampler / output device may want a different buffer size than 113 // Since the resampler / output device may want a different buffer size than
101 // the caller asked for, we need to use a FIFO to ensure that both sides 114 // the caller asked for, we need to use a FIFO to ensure that both sides
102 // read in chunk sizes they're configured for. 115 // read in chunk sizes they're configured for.
103 if (input_params.sample_rate() != output_params.sample_rate() || 116 if (params_.sample_rate() != output_params_.sample_rate() ||
104 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { 117 params_.frames_per_buffer() != output_params_.frames_per_buffer()) {
105 DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer() 118 DVLOG(1) << "Rebuffering from " << params_.frames_per_buffer()
106 << " to " << output_params.frames_per_buffer(); 119 << " to " << output_params_.frames_per_buffer();
107 audio_fifo_.reset(new AudioPullFifo( 120 audio_fifo_.reset(new AudioPullFifo(
108 input_params.channels(), input_params.frames_per_buffer(), base::Bind( 121 params_.channels(), params_.frames_per_buffer(), base::Bind(
109 &AudioOutputResampler::SourceCallback_Locked, 122 &AudioOutputResampler::SourceCallback_Locked,
110 base::Unretained(this)))); 123 base::Unretained(this))));
111 } 124 }
112 125
113 DVLOG(1) << "I/O ratio is " << io_ratio_; 126 DVLOG(1) << "I/O ratio is " << io_ratio_;
114 } 127 }
115 128
116 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once 129 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once
117 // we've stabilized the issues there. 130 // we've stabilized the issues there.
118 dispatcher_ = new AudioOutputDispatcherImpl( 131 dispatcher_ = new AudioOutputDispatcherImpl(
119 audio_manager, output_params, close_delay); 132 audio_manager_, output_params_, close_delay_);
120 133
121 // Record UMA statistics for the hardware configuration. 134 // Record UMA statistics for the hardware configuration.
122 RecordStats(output_params); 135 if (record_stats)
136 RecordStats("", output_params_);
123 } 137 }
124 138
125 AudioOutputResampler::~AudioOutputResampler() {} 139 bool AudioOutputResampler::OpenStream() {
140 if (dispatcher_->OpenStream()) {
141 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
142 return true;
143 }
126 144
127 bool AudioOutputResampler::OpenStream() { 145 // If we've already tried to open the stream in high latency mode, there's
128 // TODO(dalecurtis): Automatically revert to high latency path if OpenStream() 146 // nothing more to be done.
129 // fails; use default high latency output values + rebuffering / resampling. 147 if (output_params_.format() == AudioParameters::AUDIO_PCM_LINEAR)
148 return false;
149
150 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling "
151 << "back to high latency audio output.";
152
153 // Record UMA statistics about the hardware which triggered the failure so we
154 // can debug and triage later.
155 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
156 RecordStats("Fallback", output_params_);
157
158 // Open failed! Attempt to open the output device in high latency mode using
159 // a new high latency appropriate buffer size. |kMinLowLatencyFrameSize| is
160 // arbitrarily based on Pepper Flash's MAXIMUM frame size for low latency.
161 static const int kMinLowLatencyFrameSize = 2048;
162 int frames_per_buffer = std::max(
163 std::min(params_.frames_per_buffer(), kMinLowLatencyFrameSize),
164 static_cast<int>(GetHighLatencyOutputBufferSize(params_.sample_rate())));
165
166 output_params_ = AudioParameters(
167 AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(),
168 params_.sample_rate(), params_.bits_per_sample(), frames_per_buffer);
169 Initialize(false);
170
171 // Retry, if this fails, there's nothing left to do but report the error back.
130 return dispatcher_->OpenStream(); 172 return dispatcher_->OpenStream();
131 } 173 }
132 174
133 bool AudioOutputResampler::StartStream( 175 bool AudioOutputResampler::StartStream(
134 AudioOutputStream::AudioSourceCallback* callback, 176 AudioOutputStream::AudioSourceCallback* callback,
135 AudioOutputProxy* stream_proxy) { 177 AudioOutputProxy* stream_proxy) {
136 { 178 {
137 base::AutoLock auto_lock(source_lock_); 179 base::AutoLock auto_lock(source_lock_);
138 source_callback_ = callback; 180 source_callback_ = callback;
139 } 181 }
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 SourceCallback_Locked(dest); 235 SourceCallback_Locked(dest);
194 return dest->frames(); 236 return dest->frames();
195 } 237 }
196 238
197 if (resampler_.get()) 239 if (resampler_.get())
198 resampler_->Resample(dest, dest->frames()); 240 resampler_->Resample(dest, dest->frames());
199 else 241 else
200 ProvideInput(dest); 242 ProvideInput(dest);
201 243
202 // Calculate how much data is left in the internal FIFO and resampler buffers. 244 // Calculate how much data is left in the internal FIFO and resampler buffers.
203 outstanding_audio_bytes_ -= dest->frames() * output_bytes_per_frame_; 245 outstanding_audio_bytes_ -=
246 dest->frames() * output_params_.GetBytesPerFrame();
247
204 // Due to rounding errors while multiplying against |io_ratio_|, 248 // Due to rounding errors while multiplying against |io_ratio_|,
205 // |outstanding_audio_bytes_| might (rarely) slip below zero. 249 // |outstanding_audio_bytes_| might (rarely) slip below zero.
206 if (outstanding_audio_bytes_ < 0) { 250 if (outstanding_audio_bytes_ < 0) {
207 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: " 251 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: "
208 << outstanding_audio_bytes_; 252 << outstanding_audio_bytes_;
209 outstanding_audio_bytes_ = 0; 253 outstanding_audio_bytes_ = 0;
210 } 254 }
211 255
212 // Always return the full number of frames requested, ProvideInput() will pad 256 // Always return the full number of frames requested, ProvideInput() will pad
213 // with silence if it wasn't able to acquire enough data. 257 // with silence if it wasn't able to acquire enough data.
(...skipping 11 matching lines...) Expand all
225 (current_buffers_state_.total_bytes() + outstanding_audio_bytes_); 269 (current_buffers_state_.total_bytes() + outstanding_audio_bytes_);
226 270
227 // Retrieve data from the original callback. Zero any unfilled frames. 271 // Retrieve data from the original callback. Zero any unfilled frames.
228 int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state); 272 int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state);
229 if (frames < audio_bus->frames()) 273 if (frames < audio_bus->frames())
230 audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames); 274 audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames);
231 275
232 // Scale the number of frames we got back in terms of input bytes to output 276 // Scale the number of frames we got back in terms of input bytes to output
233 // bytes accordingly. 277 // bytes accordingly.
234 outstanding_audio_bytes_ += 278 outstanding_audio_bytes_ +=
235 (audio_bus->frames() * input_bytes_per_frame_) / io_ratio_; 279 (audio_bus->frames() * params_.GetBytesPerFrame()) / io_ratio_;
236 } 280 }
237 281
238 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { 282 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) {
239 audio_fifo_->Consume(audio_bus, audio_bus->frames()); 283 audio_fifo_->Consume(audio_bus, audio_bus->frames());
240 } 284 }
241 285
242 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) { 286 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) {
243 base::AutoLock auto_lock(source_lock_); 287 base::AutoLock auto_lock(source_lock_);
244 if (source_callback_) 288 if (source_callback_)
245 source_callback_->OnError(stream, code); 289 source_callback_->OnError(stream, code);
246 } 290 }
247 291
248 void AudioOutputResampler::WaitTillDataReady() { 292 void AudioOutputResampler::WaitTillDataReady() {
249 base::AutoLock auto_lock(source_lock_); 293 base::AutoLock auto_lock(source_lock_);
250 if (source_callback_ && !outstanding_audio_bytes_) 294 if (source_callback_ && !outstanding_audio_bytes_)
251 source_callback_->WaitTillDataReady(); 295 source_callback_->WaitTillDataReady();
252 } 296 }
253 297
254 } // namespace media 298 } // namespace media
OLDNEW
« media/audio/audio_output_proxy_unittest.cc ('K') | « media/audio/audio_output_resampler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698