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

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: 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
« no previous file with comments | « media/audio/audio_output_resampler.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 handled by the output device 37 // TODO(dalecurtis): Since channel mixing is handled by the output device
34 // right now and not by AudioOutputResampler, we need to query for hardware 38 // right now and not by AudioOutputResampler, we need to query for hardware
35 // channel information. Remove once AOR handles this, http://crbug.com/138762 39 // channel 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) {
scherkus (not reviewing) 2012/09/12 14:24:47 it it worth DCHECKing that we're in LOW_LATENCY or
DaleCurtis 2012/09/12 14:32:34 No reason it wouldn't work, so I haven't felt the
76 Initialize();
77 // Record UMA statistics for the hardware configuration.
78 RecordStats("", output_params_);
79 }
80
81 AudioOutputResampler::~AudioOutputResampler() {}
82
83 void AudioOutputResampler::Initialize() {
84 io_ratio_ = 1;
85
71 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762 86 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762
72 DCHECK_EQ(input_params.channels(), output_params.channels()); 87 DCHECK_EQ(params_.channels(), output_params_.channels());
73 // Only resample or rebuffer if the input parameters don't match the output 88 // Only resample or rebuffer if the input parameters don't match the output
74 // parameters to avoid any unnecessary work. 89 // parameters to avoid any unnecessary work.
75 if (input_params.channels() != output_params.channels() || 90 if (params_.channels() != output_params_.channels() ||
76 input_params.sample_rate() != output_params.sample_rate() || 91 params_.sample_rate() != output_params_.sample_rate() ||
77 input_params.bits_per_sample() != output_params.bits_per_sample() || 92 params_.bits_per_sample() != output_params_.bits_per_sample() ||
78 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { 93 params_.frames_per_buffer() != output_params_.frames_per_buffer()) {
79 // Only resample if necessary since it's expensive. 94 // Only resample if necessary since it's expensive.
80 if (input_params.sample_rate() != output_params.sample_rate()) { 95 if (params_.sample_rate() != output_params_.sample_rate()) {
81 DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to " 96 DVLOG(1) << "Resampling from " << params_.sample_rate() << " to "
82 << output_params.sample_rate(); 97 << output_params_.sample_rate();
83 double io_sample_rate_ratio = input_params.sample_rate() / 98 double io_sample_rate_ratio = params_.sample_rate() /
84 static_cast<double>(output_params.sample_rate()); 99 static_cast<double>(output_params_.sample_rate());
85 // Include the I/O resampling ratio in our global I/O ratio. 100 // Include the I/O resampling ratio in our global I/O ratio.
86 io_ratio_ *= io_sample_rate_ratio; 101 io_ratio_ *= io_sample_rate_ratio;
87 resampler_.reset(new MultiChannelResampler( 102 resampler_.reset(new MultiChannelResampler(
88 output_params.channels(), io_sample_rate_ratio, base::Bind( 103 output_params_.channels(), io_sample_rate_ratio, base::Bind(
89 &AudioOutputResampler::ProvideInput, base::Unretained(this)))); 104 &AudioOutputResampler::ProvideInput, base::Unretained(this))));
90 } 105 }
91 106
92 // Include bits per channel differences. 107 // Include bits per channel differences.
93 io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) / 108 io_ratio_ *= static_cast<double>(params_.bits_per_sample()) /
94 output_params.bits_per_sample(); 109 output_params_.bits_per_sample();
95 110
96 // Include channel count differences. 111 // Include channel count differences.
97 io_ratio_ *= static_cast<double>(input_params.channels()) / 112 io_ratio_ *= static_cast<double>(params_.channels()) /
98 output_params.channels(); 113 output_params_.channels();
99 114
100 // Since the resampler / output device may want a different buffer size than 115 // 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 116 // the caller asked for, we need to use a FIFO to ensure that both sides
102 // read in chunk sizes they're configured for. 117 // read in chunk sizes they're configured for.
103 if (input_params.sample_rate() != output_params.sample_rate() || 118 if (params_.sample_rate() != output_params_.sample_rate() ||
104 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { 119 params_.frames_per_buffer() != output_params_.frames_per_buffer()) {
105 DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer() 120 DVLOG(1) << "Rebuffering from " << params_.frames_per_buffer()
106 << " to " << output_params.frames_per_buffer(); 121 << " to " << output_params_.frames_per_buffer();
107 audio_fifo_.reset(new AudioPullFifo( 122 audio_fifo_.reset(new AudioPullFifo(
108 input_params.channels(), input_params.frames_per_buffer(), base::Bind( 123 params_.channels(), params_.frames_per_buffer(), base::Bind(
109 &AudioOutputResampler::SourceCallback_Locked, 124 &AudioOutputResampler::SourceCallback_Locked,
110 base::Unretained(this)))); 125 base::Unretained(this))));
111 } 126 }
112 127
113 DVLOG(1) << "I/O ratio is " << io_ratio_; 128 DVLOG(1) << "I/O ratio is " << io_ratio_;
114 } 129 }
115 130
116 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once 131 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once
117 // we've stabilized the issues there. 132 // we've stabilized the issues there.
118 dispatcher_ = new AudioOutputDispatcherImpl( 133 dispatcher_ = new AudioOutputDispatcherImpl(
119 audio_manager, output_params, close_delay); 134 audio_manager_, output_params_, close_delay_);
120
121 // Record UMA statistics for the hardware configuration.
122 RecordStats(output_params);
123 } 135 }
124 136
125 AudioOutputResampler::~AudioOutputResampler() {} 137 bool AudioOutputResampler::OpenStream() {
138 if (dispatcher_->OpenStream()) {
139 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
140 return true;
141 }
126 142
127 bool AudioOutputResampler::OpenStream() { 143 // 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() 144 // nothing more to be done.
129 // fails; use default high latency output values + rebuffering / resampling. 145 if (output_params_.format() == AudioParameters::AUDIO_PCM_LINEAR)
146 return false;
147
148 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling "
149 << "back to high latency audio output.";
150
151 // Record UMA statistics about the hardware which triggered the failure so we
152 // can debug and triage later.
153 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
154 RecordStats("Fallback", output_params_);
155
156 // Open failed! Attempt to open the output device in high latency mode using
157 // a new high latency appropriate buffer size. |kMinLowLatencyFrameSize| is
158 // arbitrarily based on Pepper Flash's MAXIMUM frame size for low latency.
159 static const int kMinLowLatencyFrameSize = 2048;
160 int frames_per_buffer = std::max(
161 std::min(params_.frames_per_buffer(), kMinLowLatencyFrameSize),
162 static_cast<int>(GetHighLatencyOutputBufferSize(params_.sample_rate())));
163
164 output_params_ = AudioParameters(
165 AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(),
166 params_.sample_rate(), params_.bits_per_sample(), frames_per_buffer);
167 Initialize();
168
169 // Retry, if this fails, there's nothing left to do but report the error back.
130 return dispatcher_->OpenStream(); 170 return dispatcher_->OpenStream();
131 } 171 }
132 172
133 bool AudioOutputResampler::StartStream( 173 bool AudioOutputResampler::StartStream(
134 AudioOutputStream::AudioSourceCallback* callback, 174 AudioOutputStream::AudioSourceCallback* callback,
135 AudioOutputProxy* stream_proxy) { 175 AudioOutputProxy* stream_proxy) {
136 { 176 {
137 base::AutoLock auto_lock(source_lock_); 177 base::AutoLock auto_lock(source_lock_);
138 source_callback_ = callback; 178 source_callback_ = callback;
139 } 179 }
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 SourceCallback_Locked(dest); 233 SourceCallback_Locked(dest);
194 return dest->frames(); 234 return dest->frames();
195 } 235 }
196 236
197 if (resampler_.get()) 237 if (resampler_.get())
198 resampler_->Resample(dest, dest->frames()); 238 resampler_->Resample(dest, dest->frames());
199 else 239 else
200 ProvideInput(dest); 240 ProvideInput(dest);
201 241
202 // Calculate how much data is left in the internal FIFO and resampler buffers. 242 // Calculate how much data is left in the internal FIFO and resampler buffers.
203 outstanding_audio_bytes_ -= dest->frames() * output_bytes_per_frame_; 243 outstanding_audio_bytes_ -=
244 dest->frames() * output_params_.GetBytesPerFrame();
245
204 // Due to rounding errors while multiplying against |io_ratio_|, 246 // Due to rounding errors while multiplying against |io_ratio_|,
205 // |outstanding_audio_bytes_| might (rarely) slip below zero. 247 // |outstanding_audio_bytes_| might (rarely) slip below zero.
206 if (outstanding_audio_bytes_ < 0) { 248 if (outstanding_audio_bytes_ < 0) {
207 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: " 249 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: "
208 << outstanding_audio_bytes_; 250 << outstanding_audio_bytes_;
209 outstanding_audio_bytes_ = 0; 251 outstanding_audio_bytes_ = 0;
210 } 252 }
211 253
212 // Always return the full number of frames requested, ProvideInput() will pad 254 // Always return the full number of frames requested, ProvideInput() will pad
213 // with silence if it wasn't able to acquire enough data. 255 // 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_); 267 (current_buffers_state_.total_bytes() + outstanding_audio_bytes_);
226 268
227 // Retrieve data from the original callback. Zero any unfilled frames. 269 // Retrieve data from the original callback. Zero any unfilled frames.
228 int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state); 270 int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state);
229 if (frames < audio_bus->frames()) 271 if (frames < audio_bus->frames())
230 audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames); 272 audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames);
231 273
232 // Scale the number of frames we got back in terms of input bytes to output 274 // Scale the number of frames we got back in terms of input bytes to output
233 // bytes accordingly. 275 // bytes accordingly.
234 outstanding_audio_bytes_ += 276 outstanding_audio_bytes_ +=
235 (audio_bus->frames() * input_bytes_per_frame_) / io_ratio_; 277 (audio_bus->frames() * params_.GetBytesPerFrame()) / io_ratio_;
236 } 278 }
237 279
238 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { 280 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) {
239 audio_fifo_->Consume(audio_bus, audio_bus->frames()); 281 audio_fifo_->Consume(audio_bus, audio_bus->frames());
240 } 282 }
241 283
242 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) { 284 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) {
243 base::AutoLock auto_lock(source_lock_); 285 base::AutoLock auto_lock(source_lock_);
244 if (source_callback_) 286 if (source_callback_)
245 source_callback_->OnError(stream, code); 287 source_callback_->OnError(stream, code);
246 } 288 }
247 289
248 void AudioOutputResampler::WaitTillDataReady() { 290 void AudioOutputResampler::WaitTillDataReady() {
249 base::AutoLock auto_lock(source_lock_); 291 base::AutoLock auto_lock(source_lock_);
250 if (source_callback_ && !outstanding_audio_bytes_) 292 if (source_callback_ && !outstanding_audio_bytes_)
251 source_callback_->WaitTillDataReady(); 293 source_callback_->WaitTillDataReady();
252 } 294 }
253 295
254 } // namespace media 296 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/audio_output_resampler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698