Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / media / audio / audio_output_resampler.cc
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/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"
20
21 namespace media {
22
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();
30
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;
38
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);
42
43   // Clears |source_callback_| and flushes the resampler.
44   void Stop();
45
46   bool started() { return source_callback_ != NULL; }
47
48  private:
49   // AudioConverter::InputCallback implementation.
50   virtual double ProvideInput(AudioBus* audio_bus,
51                               base::TimeDelta buffer_delay) OVERRIDE;
52
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_;
56
57   // Source callback.
58   AudioOutputStream::AudioSourceCallback* source_callback_;
59
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_;
63
64   const int input_bytes_per_second_;
65
66   // Handles resampling, buffering, and channel mixing between input and output
67   // parameters.
68   AudioConverter audio_converter_;
69
70   DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
71 };
72
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
88
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());
97   }
98 }
99
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
116
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());
126   }
127 }
128
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);
141
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
149 }
150
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);
163
164   // Record UMA statistics for the hardware configuration.
165   RecordStats(output_params);
166
167   Initialize();
168 }
169
170 AudioOutputResampler::~AudioOutputResampler() {
171   DCHECK(callbacks_.empty());
172 }
173
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_);
179 }
180
181 bool AudioOutputResampler::OpenStream() {
182   DCHECK(task_runner_->BelongsToCurrentThread());
183
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);
190     }
191     streams_opened_ = true;
192     return true;
193   }
194
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;
200   }
201
202   DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
203
204   // Record UMA statistics about the hardware which triggered the failure so
205   // we can debug and triage later.
206   RecordFallbackStats(output_params_);
207
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.";
213
214   SetupFallbackParams();
215   if (dispatcher_->OpenStream()) {
216     streams_opened_ = true;
217     return true;
218   }
219 #endif
220
221   DLOG(ERROR) << "Unable to open audio device in high latency mode.  Falling "
222               << "back to fake audio output.";
223
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;
233   }
234
235   return false;
236 }
237
238 bool AudioOutputResampler::StartStream(
239     AudioOutputStream::AudioSourceCallback* callback,
240     AudioOutputProxy* stream_proxy) {
241   DCHECK(task_runner_->BelongsToCurrentThread());
242
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;
250   }
251
252   resampler_callback->Start(callback);
253   bool result = dispatcher_->StartStream(resampler_callback, stream_proxy);
254   if (!result)
255     resampler_callback->Stop();
256   return result;
257 }
258
259 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
260                                            double volume) {
261   DCHECK(task_runner_->BelongsToCurrentThread());
262   dispatcher_->StreamVolumeSet(stream_proxy, volume);
263 }
264
265 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
266   DCHECK(task_runner_->BelongsToCurrentThread());
267   dispatcher_->StopStream(stream_proxy);
268
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();
275 }
276
277 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
278   DCHECK(task_runner_->BelongsToCurrentThread());
279   dispatcher_->CloseStream(stream_proxy);
280
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);
287   }
288 }
289
290 void AudioOutputResampler::Shutdown() {
291   DCHECK(task_runner_->BelongsToCurrentThread());
292
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";
296
297   dispatcher_->Shutdown();
298   DCHECK(callbacks_.empty());
299 }
300
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) {}
308
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_);
313 }
314
315 void OnMoreDataConverter::Start(
316     AudioOutputStream::AudioSourceCallback* callback) {
317   CHECK(!source_callback_);
318   source_callback_ = callback;
319
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);
324 }
325
326 void OnMoreDataConverter::Stop() {
327   CHECK(source_callback_);
328   source_callback_ = NULL;
329   audio_converter_.RemoveInput(this);
330 }
331
332 int OnMoreDataConverter::OnMoreData(AudioBus* dest,
333                                     AudioBuffersState buffers_state) {
334   return OnMoreIOData(NULL, dest, buffers_state);
335 }
336
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.
343
344   current_buffers_state_ = buffers_state;
345   audio_converter_.Convert(dest);
346
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();
350 }
351
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_);
361
362   // Retrieve data from the original callback.
363   const int frames = source_callback_->OnMoreIOData(
364       NULL, dest, new_buffers_state);
365
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;
371 }
372
373 void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
374   source_callback_->OnError(stream);
375 }
376
377 }  // namespace media