Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / media / audio / audio_output_resampler.cc
blob4863351614f43c995d7045b8d026e6721fbfb568
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/metrics/histogram.h"
11 #include "base/single_thread_task_runner.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 bool started() { return source_callback_ != NULL; }
48 private:
49 // AudioConverter::InputCallback implementation.
50 virtual double ProvideInput(AudioBus* audio_bus,
51 base::TimeDelta buffer_delay) OVERRIDE;
53 // Ratio of input bytes to output bytes used to correct playback delay with
54 // regard to buffering and resampling.
55 const double io_ratio_;
57 // Source callback.
58 AudioOutputStream::AudioSourceCallback* source_callback_;
60 // Last AudioBuffersState object received via OnMoreData(), used to correct
61 // playback delay by ProvideInput() and passed on to |source_callback_|.
62 AudioBuffersState current_buffers_state_;
64 const int input_bytes_per_second_;
66 // Handles resampling, buffering, and channel mixing between input and output
67 // parameters.
68 AudioConverter audio_converter_;
70 DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
73 // Record UMA statistics for hardware output configuration.
74 static void RecordStats(const AudioParameters& output_params) {
75 // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
76 // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
77 // to report a discrete value.
78 UMA_HISTOGRAM_ENUMERATION(
79 "Media.HardwareAudioBitsPerChannel",
80 output_params.bits_per_sample(),
81 limits::kMaxBitsPerSample); // PRESUBMIT_IGNORE_UMA_MAX
82 UMA_HISTOGRAM_ENUMERATION(
83 "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
84 CHANNEL_LAYOUT_MAX + 1);
85 UMA_HISTOGRAM_ENUMERATION(
86 "Media.HardwareAudioChannelCount", output_params.channels(),
87 limits::kMaxChannels); // PRESUBMIT_IGNORE_UMA_MAX
89 AudioSampleRate asr;
90 if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
91 UMA_HISTOGRAM_ENUMERATION(
92 "Media.HardwareAudioSamplesPerSecond", asr, kAudioSampleRateMax + 1);
93 } else {
94 UMA_HISTOGRAM_COUNTS(
95 "Media.HardwareAudioSamplesPerSecondUnexpected",
96 output_params.sample_rate());
100 // Record UMA statistics for hardware output configuration after fallback.
101 static void RecordFallbackStats(const AudioParameters& output_params) {
102 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
103 // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
104 // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
105 // to report a discrete value.
106 UMA_HISTOGRAM_ENUMERATION(
107 "Media.FallbackHardwareAudioBitsPerChannel",
108 output_params.bits_per_sample(),
109 limits::kMaxBitsPerSample); // PRESUBMIT_IGNORE_UMA_MAX
110 UMA_HISTOGRAM_ENUMERATION(
111 "Media.FallbackHardwareAudioChannelLayout",
112 output_params.channel_layout(), CHANNEL_LAYOUT_MAX + 1);
113 UMA_HISTOGRAM_ENUMERATION(
114 "Media.FallbackHardwareAudioChannelCount", output_params.channels(),
115 limits::kMaxChannels); // PRESUBMIT_IGNORE_UMA_MAX
117 AudioSampleRate asr;
118 if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
119 UMA_HISTOGRAM_ENUMERATION(
120 "Media.FallbackHardwareAudioSamplesPerSecond",
121 asr, kAudioSampleRateMax + 1);
122 } else {
123 UMA_HISTOGRAM_COUNTS(
124 "Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
125 output_params.sample_rate());
129 // Converts low latency based |output_params| into high latency appropriate
130 // output parameters in error situations.
131 void AudioOutputResampler::SetupFallbackParams() {
132 // Only Windows has a high latency output driver that is not the same as the low
133 // latency path.
134 #if defined(OS_WIN)
135 // Choose AudioParameters appropriate for opening the device in high latency
136 // mode. |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
137 // MAXIMUM frame size for low latency.
138 static const int kMinLowLatencyFrameSize = 2048;
139 const int frames_per_buffer =
140 std::max(params_.frames_per_buffer(), kMinLowLatencyFrameSize);
142 output_params_ = AudioParameters(
143 AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(),
144 params_.sample_rate(), params_.bits_per_sample(),
145 frames_per_buffer);
146 device_id_ = "";
147 Initialize();
148 #endif
151 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
152 const AudioParameters& input_params,
153 const AudioParameters& output_params,
154 const std::string& output_device_id,
155 const base::TimeDelta& close_delay)
156 : AudioOutputDispatcher(audio_manager, input_params, output_device_id),
157 close_delay_(close_delay),
158 output_params_(output_params),
159 streams_opened_(false) {
160 DCHECK(input_params.IsValid());
161 DCHECK(output_params.IsValid());
162 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
164 // Record UMA statistics for the hardware configuration.
165 RecordStats(output_params);
167 Initialize();
170 AudioOutputResampler::~AudioOutputResampler() {
171 DCHECK(callbacks_.empty());
174 void AudioOutputResampler::Initialize() {
175 DCHECK(!streams_opened_);
176 DCHECK(callbacks_.empty());
177 dispatcher_ = new AudioOutputDispatcherImpl(
178 audio_manager_, output_params_, device_id_, close_delay_);
181 bool AudioOutputResampler::OpenStream() {
182 DCHECK(task_runner_->BelongsToCurrentThread());
184 if (dispatcher_->OpenStream()) {
185 // Only record the UMA statistic if we didn't fallback during construction
186 // and only for the first stream we open.
187 if (!streams_opened_ &&
188 output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
189 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
191 streams_opened_ = true;
192 return true;
195 // If we've already tried to open the stream in high latency mode or we've
196 // successfully opened a stream previously, there's nothing more to be done.
197 if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY ||
198 streams_opened_ || !callbacks_.empty()) {
199 return false;
202 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
204 // Record UMA statistics about the hardware which triggered the failure so
205 // we can debug and triage later.
206 RecordFallbackStats(output_params_);
208 // Only Windows has a high latency output driver that is not the same as the
209 // low latency path.
210 #if defined(OS_WIN)
211 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling "
212 << "back to high latency audio output.";
214 SetupFallbackParams();
215 if (dispatcher_->OpenStream()) {
216 streams_opened_ = true;
217 return true;
219 #endif
221 DLOG(ERROR) << "Unable to open audio device in high latency mode. Falling "
222 << "back to fake audio output.";
224 // Finally fall back to a fake audio output device.
225 output_params_.Reset(
226 AudioParameters::AUDIO_FAKE, params_.channel_layout(),
227 params_.channels(), params_.input_channels(), params_.sample_rate(),
228 params_.bits_per_sample(), params_.frames_per_buffer());
229 Initialize();
230 if (dispatcher_->OpenStream()) {
231 streams_opened_ = true;
232 return true;
235 return false;
238 bool AudioOutputResampler::StartStream(
239 AudioOutputStream::AudioSourceCallback* callback,
240 AudioOutputProxy* stream_proxy) {
241 DCHECK(task_runner_->BelongsToCurrentThread());
243 OnMoreDataConverter* resampler_callback = NULL;
244 CallbackMap::iterator it = callbacks_.find(stream_proxy);
245 if (it == callbacks_.end()) {
246 resampler_callback = new OnMoreDataConverter(params_, output_params_);
247 callbacks_[stream_proxy] = resampler_callback;
248 } else {
249 resampler_callback = it->second;
252 resampler_callback->Start(callback);
253 bool result = dispatcher_->StartStream(resampler_callback, stream_proxy);
254 if (!result)
255 resampler_callback->Stop();
256 return result;
259 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
260 double volume) {
261 DCHECK(task_runner_->BelongsToCurrentThread());
262 dispatcher_->StreamVolumeSet(stream_proxy, volume);
265 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
266 DCHECK(task_runner_->BelongsToCurrentThread());
267 dispatcher_->StopStream(stream_proxy);
269 // Now that StopStream() has completed the underlying physical stream should
270 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
271 // OnMoreDataConverter.
272 CallbackMap::iterator it = callbacks_.find(stream_proxy);
273 if (it != callbacks_.end())
274 it->second->Stop();
277 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
278 DCHECK(task_runner_->BelongsToCurrentThread());
279 dispatcher_->CloseStream(stream_proxy);
281 // We assume that StopStream() is always called prior to CloseStream(), so
282 // that it is safe to delete the OnMoreDataConverter here.
283 CallbackMap::iterator it = callbacks_.find(stream_proxy);
284 if (it != callbacks_.end()) {
285 delete it->second;
286 callbacks_.erase(it);
290 void AudioOutputResampler::Shutdown() {
291 DCHECK(task_runner_->BelongsToCurrentThread());
293 // No AudioOutputProxy objects should hold a reference to us when we get
294 // to this stage.
295 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
297 dispatcher_->Shutdown();
298 DCHECK(callbacks_.empty());
301 OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
302 const AudioParameters& output_params)
303 : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
304 output_params.GetBytesPerSecond()),
305 source_callback_(NULL),
306 input_bytes_per_second_(input_params.GetBytesPerSecond()),
307 audio_converter_(input_params, output_params, false) {}
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 CHECK(!source_callback_);
318 source_callback_ = callback;
320 // While AudioConverter can handle multiple inputs, we're using it only with
321 // a single input currently. Eventually this may be the basis for a browser
322 // side mixer.
323 audio_converter_.AddInput(this);
326 void OnMoreDataConverter::Stop() {
327 CHECK(source_callback_);
328 source_callback_ = NULL;
329 audio_converter_.RemoveInput(this);
332 int OnMoreDataConverter::OnMoreData(AudioBus* dest,
333 AudioBuffersState buffers_state) {
334 return OnMoreIOData(NULL, dest, buffers_state);
337 int OnMoreDataConverter::OnMoreIOData(AudioBus* source,
338 AudioBus* dest,
339 AudioBuffersState buffers_state) {
340 // Note: The input portion of OnMoreIOData() is not supported when a converter
341 // has been injected. Downstream clients prefer silence to potentially split
342 // apart input data.
344 current_buffers_state_ = buffers_state;
345 audio_converter_.Convert(dest);
347 // Always return the full number of frames requested, ProvideInput()
348 // will pad with silence if it wasn't able to acquire enough data.
349 return dest->frames();
352 double OnMoreDataConverter::ProvideInput(AudioBus* dest,
353 base::TimeDelta buffer_delay) {
354 // Adjust playback delay to include |buffer_delay|.
355 // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
356 // AudioBus is just float data. Use TimeDelta instead.
357 AudioBuffersState new_buffers_state;
358 new_buffers_state.pending_bytes =
359 io_ratio_ * (current_buffers_state_.total_bytes() +
360 buffer_delay.InSecondsF() * input_bytes_per_second_);
362 // Retrieve data from the original callback.
363 const int frames = source_callback_->OnMoreIOData(
364 NULL, dest, new_buffers_state);
366 // Zero any unfilled frames if anything was filled, otherwise we'll just
367 // return a volume of zero and let AudioConverter drop the output.
368 if (frames > 0 && frames < dest->frames())
369 dest->ZeroFramesPartial(frames, dest->frames() - frames);
370 return frames > 0 ? 1 : 0;
373 void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
374 source_callback_->OnError(stream);
377 } // namespace media