audio_output_controller.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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_controller.h"
6
7#include "base/bind.h"
8#include "base/debug/trace_event.h"
9#include "base/metrics/histogram.h"
10#include "base/task_runner_util.h"
11#include "base/threading/platform_thread.h"
12#include "base/time/time.h"
13#include "build/build_config.h"
14#include "media/base/scoped_histogram_timer.h"
15
16using base::TimeDelta;
17
18namespace media {
19
20#if defined(AUDIO_POWER_MONITORING)
21// Time constant for AudioPowerMonitor.  See AudioPowerMonitor ctor comments for
22// semantics.  This value was arbitrarily chosen, but seems to work well.
23static const int kPowerMeasurementTimeConstantMillis = 10;
24
25// Desired frequency of calls to EventHandler::OnPowerMeasured() for reporting
26// power levels in the audio signal.
27static const int kPowerMeasurementsPerSecond = 4;
28#endif
29
30// Polling-related constants.
31const int AudioOutputController::kPollNumAttempts = 3;
32const int AudioOutputController::kPollPauseInMilliseconds = 3;
33
34AudioOutputController::AudioOutputController(
35    AudioManager* audio_manager,
36    EventHandler* handler,
37    const AudioParameters& params,
38    const std::string& output_device_id,
39    SyncReader* sync_reader)
40    : audio_manager_(audio_manager),
41      params_(params),
42      handler_(handler),
43      output_device_id_(output_device_id),
44      stream_(NULL),
45      diverting_to_stream_(NULL),
46      volume_(1.0),
47      state_(kEmpty),
48      num_allowed_io_(0),
49      sync_reader_(sync_reader),
50      message_loop_(audio_manager->GetTaskRunner()),
51#if defined(AUDIO_POWER_MONITORING)
52      power_monitor_(
53          params.sample_rate(),
54          TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)),
55#endif
56      on_more_io_data_called_(0) {
57  DCHECK(audio_manager);
58  DCHECK(handler_);
59  DCHECK(sync_reader_);
60  DCHECK(message_loop_.get());
61}
62
63AudioOutputController::~AudioOutputController() {
64  DCHECK_EQ(kClosed, state_);
65}
66
67// static
68scoped_refptr<AudioOutputController> AudioOutputController::Create(
69    AudioManager* audio_manager,
70    EventHandler* event_handler,
71    const AudioParameters& params,
72    const std::string& output_device_id,
73    SyncReader* sync_reader) {
74  DCHECK(audio_manager);
75  DCHECK(sync_reader);
76
77  if (!params.IsValid() || !audio_manager)
78    return NULL;
79
80  scoped_refptr<AudioOutputController> controller(new AudioOutputController(
81      audio_manager, event_handler, params, output_device_id, sync_reader));
82  controller->message_loop_->PostTask(FROM_HERE, base::Bind(
83      &AudioOutputController::DoCreate, controller, false));
84  return controller;
85}
86
87void AudioOutputController::Play() {
88  message_loop_->PostTask(FROM_HERE, base::Bind(
89      &AudioOutputController::DoPlay, this));
90}
91
92void AudioOutputController::Pause() {
93  message_loop_->PostTask(FROM_HERE, base::Bind(
94      &AudioOutputController::DoPause, this));
95}
96
97void AudioOutputController::Close(const base::Closure& closed_task) {
98  DCHECK(!closed_task.is_null());
99  message_loop_->PostTaskAndReply(FROM_HERE, base::Bind(
100      &AudioOutputController::DoClose, this), closed_task);
101}
102
103void AudioOutputController::SetVolume(double volume) {
104  message_loop_->PostTask(FROM_HERE, base::Bind(
105      &AudioOutputController::DoSetVolume, this, volume));
106}
107
108void AudioOutputController::GetOutputDeviceId(
109    base::Callback<void(const std::string&)> callback) const {
110  base::PostTaskAndReplyWithResult(
111      message_loop_.get(),
112      FROM_HERE,
113      base::Bind(&AudioOutputController::DoGetOutputDeviceId, this),
114      callback);
115}
116
117void AudioOutputController::SwitchOutputDevice(
118    const std::string& output_device_id, const base::Closure& callback) {
119  message_loop_->PostTaskAndReply(
120      FROM_HERE,
121      base::Bind(&AudioOutputController::DoSwitchOutputDevice, this,
122                 output_device_id),
123      callback);
124}
125
126void AudioOutputController::DoCreate(bool is_for_device_change) {
127  DCHECK(message_loop_->BelongsToCurrentThread());
128  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime");
129  TRACE_EVENT0("audio", "AudioOutputController::DoCreate");
130
131  // Close() can be called before DoCreate() is executed.
132  if (state_ == kClosed)
133    return;
134
135  DoStopCloseAndClearStream();  // Calls RemoveOutputDeviceChangeListener().
136  DCHECK_EQ(kEmpty, state_);
137
138  stream_ = diverting_to_stream_ ?
139      diverting_to_stream_ :
140      audio_manager_->MakeAudioOutputStreamProxy(params_, output_device_id_);
141  if (!stream_) {
142    state_ = kError;
143    handler_->OnError();
144    return;
145  }
146
147  if (!stream_->Open()) {
148    DoStopCloseAndClearStream();
149    state_ = kError;
150    handler_->OnError();
151    return;
152  }
153
154  // Everything started okay, so re-register for state change callbacks if
155  // stream_ was created via AudioManager.
156  if (stream_ != diverting_to_stream_)
157    audio_manager_->AddOutputDeviceChangeListener(this);
158
159  // We have successfully opened the stream. Set the initial volume.
160  stream_->SetVolume(volume_);
161
162  // Finally set the state to kCreated.
163  state_ = kCreated;
164
165  // And then report we have been created if we haven't done so already.
166  if (!is_for_device_change)
167    handler_->OnCreated();
168}
169
170void AudioOutputController::DoPlay() {
171  DCHECK(message_loop_->BelongsToCurrentThread());
172  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime");
173  TRACE_EVENT0("audio", "AudioOutputController::DoPlay");
174
175  // We can start from created or paused state.
176  if (state_ != kCreated && state_ != kPaused)
177    return;
178
179  // Ask for first packet.
180  sync_reader_->UpdatePendingBytes(0);
181
182  state_ = kPlaying;
183
184#if defined(AUDIO_POWER_MONITORING)
185  power_monitor_.Reset();
186  power_poll_callback_.Reset(
187      base::Bind(&AudioOutputController::ReportPowerMeasurementPeriodically,
188                 this));
189  // Run the callback to send an initial notification that we're starting in
190  // silence, and to schedule periodic callbacks.
191  power_poll_callback_.callback().Run();
192#endif
193
194  on_more_io_data_called_ = 0;
195  AllowEntryToOnMoreIOData();
196  stream_->Start(this);
197
198  // For UMA tracking purposes, start the wedge detection timer.  This allows us
199  // to record statistics about the number of wedged playbacks in the field.
200  //
201  // WedgeCheck() will look to see if |on_more_io_data_called_| is true after
202  // the timeout expires.  Care must be taken to ensure the wedge check delay is
203  // large enough that the value isn't queried while OnMoreDataIO() is setting
204  // it.
205  //
206  // Timer self-manages its lifetime and WedgeCheck() will only record the UMA
207  // statistic if state is still kPlaying.  Additional Start() calls will
208  // invalidate the previous timer.
209  wedge_timer_.reset(new base::OneShotTimer<AudioOutputController>());
210  wedge_timer_->Start(
211      FROM_HERE, TimeDelta::FromSeconds(5), this,
212      &AudioOutputController::WedgeCheck);
213
214  handler_->OnPlaying();
215}
216
217#if defined(AUDIO_POWER_MONITORING)
218void AudioOutputController::ReportPowerMeasurementPeriodically() {
219  DCHECK(message_loop_->BelongsToCurrentThread());
220  const std::pair<float, bool>& reading =
221      power_monitor_.ReadCurrentPowerAndClip();
222  handler_->OnPowerMeasured(reading.first, reading.second);
223  message_loop_->PostDelayedTask(
224      FROM_HERE, power_poll_callback_.callback(),
225      TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond);
226}
227#endif
228
229void AudioOutputController::StopStream() {
230  DCHECK(message_loop_->BelongsToCurrentThread());
231
232  if (state_ == kPlaying) {
233    wedge_timer_.reset();
234    stream_->Stop();
235    DisallowEntryToOnMoreIOData();
236
237#if defined(AUDIO_POWER_MONITORING)
238    power_poll_callback_.Cancel();
239#endif
240
241    state_ = kPaused;
242  }
243}
244
245void AudioOutputController::DoPause() {
246  DCHECK(message_loop_->BelongsToCurrentThread());
247  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime");
248  TRACE_EVENT0("audio", "AudioOutputController::DoPause");
249
250  StopStream();
251
252  if (state_ != kPaused)
253    return;
254
255  // Let the renderer know we've stopped.  Necessary to let PPAPI clients know
256  // audio has been shutdown.  TODO(dalecurtis): This stinks.  PPAPI should have
257  // a better way to know when it should exit PPB_Audio_Shared::Run().
258  sync_reader_->UpdatePendingBytes(-1);
259
260#if defined(AUDIO_POWER_MONITORING)
261  // Paused means silence follows.
262  handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false);
263#endif
264
265  handler_->OnPaused();
266}
267
268void AudioOutputController::DoClose() {
269  DCHECK(message_loop_->BelongsToCurrentThread());
270  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime");
271  TRACE_EVENT0("audio", "AudioOutputController::DoClose");
272
273  if (state_ != kClosed) {
274    DoStopCloseAndClearStream();
275    sync_reader_->Close();
276    state_ = kClosed;
277  }
278}
279
280void AudioOutputController::DoSetVolume(double volume) {
281  DCHECK(message_loop_->BelongsToCurrentThread());
282
283  // Saves the volume to a member first. We may not be able to set the volume
284  // right away but when the stream is created we'll set the volume.
285  volume_ = volume;
286
287  switch (state_) {
288    case kCreated:
289    case kPlaying:
290    case kPaused:
291      stream_->SetVolume(volume_);
292      break;
293    default:
294      return;
295  }
296}
297
298std::string AudioOutputController::DoGetOutputDeviceId() const {
299  DCHECK(message_loop_->BelongsToCurrentThread());
300  return output_device_id_;
301}
302
303void AudioOutputController::DoSwitchOutputDevice(
304    const std::string& output_device_id) {
305  DCHECK(message_loop_->BelongsToCurrentThread());
306
307  if (state_ == kClosed)
308    return;
309
310  if (output_device_id == output_device_id_)
311    return;
312
313  output_device_id_ = output_device_id;
314
315  // If output is currently diverted, we must not call OnDeviceChange
316  // since it would break the diverted setup. Once diversion is
317  // finished using StopDiverting() the output will switch to the new
318  // device ID.
319  if (stream_ != diverting_to_stream_)
320    OnDeviceChange();
321}
322
323void AudioOutputController::DoReportError() {
324  DCHECK(message_loop_->BelongsToCurrentThread());
325  if (state_ != kClosed)
326    handler_->OnError();
327}
328
329int AudioOutputController::OnMoreData(AudioBus* dest,
330                                      AudioBuffersState buffers_state) {
331  return OnMoreIOData(NULL, dest, buffers_state);
332}
333
334int AudioOutputController::OnMoreIOData(AudioBus* source,
335                                        AudioBus* dest,
336                                        AudioBuffersState buffers_state) {
337  DisallowEntryToOnMoreIOData();
338  TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData");
339
340  // Indicate that we haven't wedged (at least not indefinitely, WedgeCheck()
341  // may have already fired if OnMoreIOData() took an abnormal amount of time).
342  // Since this thread is the only writer of |on_more_io_data_called_| once the
343  // thread starts, its safe to compare and then increment.
344  if (base::AtomicRefCountIsZero(&on_more_io_data_called_))
345    base::AtomicRefCountInc(&on_more_io_data_called_);
346
347  sync_reader_->Read(source, dest);
348
349  const int frames = dest->frames();
350  sync_reader_->UpdatePendingBytes(
351      buffers_state.total_bytes() + frames * params_.GetBytesPerFrame());
352
353#if defined(AUDIO_POWER_MONITORING)
354  power_monitor_.Scan(*dest, frames);
355#endif
356
357  AllowEntryToOnMoreIOData();
358  return frames;
359}
360
361void AudioOutputController::OnError(AudioOutputStream* stream) {
362  // Handle error on the audio controller thread.
363  message_loop_->PostTask(FROM_HERE, base::Bind(
364      &AudioOutputController::DoReportError, this));
365}
366
367void AudioOutputController::DoStopCloseAndClearStream() {
368  DCHECK(message_loop_->BelongsToCurrentThread());
369
370  // Allow calling unconditionally and bail if we don't have a stream_ to close.
371  if (stream_) {
372    // De-register from state change callbacks if stream_ was created via
373    // AudioManager.
374    if (stream_ != diverting_to_stream_)
375      audio_manager_->RemoveOutputDeviceChangeListener(this);
376
377    StopStream();
378    stream_->Close();
379    if (stream_ == diverting_to_stream_)
380      diverting_to_stream_ = NULL;
381    stream_ = NULL;
382  }
383
384  state_ = kEmpty;
385}
386
387void AudioOutputController::OnDeviceChange() {
388  DCHECK(message_loop_->BelongsToCurrentThread());
389  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime");
390  TRACE_EVENT0("audio", "AudioOutputController::OnDeviceChange");
391
392  // TODO(dalecurtis): Notify the renderer side that a device change has
393  // occurred.  Currently querying the hardware information here will lead to
394  // crashes on OSX.  See http://crbug.com/158170.
395
396  // Recreate the stream (DoCreate() will first shut down an existing stream).
397  // Exit if we ran into an error.
398  const State original_state = state_;
399  DoCreate(true);
400  if (!stream_ || state_ == kError)
401    return;
402
403  // Get us back to the original state or an equivalent state.
404  switch (original_state) {
405    case kPlaying:
406      DoPlay();
407      return;
408    case kCreated:
409    case kPaused:
410      // From the outside these two states are equivalent.
411      return;
412    default:
413      NOTREACHED() << "Invalid original state.";
414  }
415}
416
417const AudioParameters& AudioOutputController::GetAudioParameters() {
418  return params_;
419}
420
421void AudioOutputController::StartDiverting(AudioOutputStream* to_stream) {
422  message_loop_->PostTask(
423      FROM_HERE,
424      base::Bind(&AudioOutputController::DoStartDiverting, this, to_stream));
425}
426
427void AudioOutputController::StopDiverting() {
428  message_loop_->PostTask(
429      FROM_HERE, base::Bind(&AudioOutputController::DoStopDiverting, this));
430}
431
432void AudioOutputController::DoStartDiverting(AudioOutputStream* to_stream) {
433  DCHECK(message_loop_->BelongsToCurrentThread());
434
435  if (state_ == kClosed)
436    return;
437
438  DCHECK(!diverting_to_stream_);
439  diverting_to_stream_ = to_stream;
440  // Note: OnDeviceChange() will engage the "re-create" process, which will
441  // detect and use the alternate AudioOutputStream rather than create a new one
442  // via AudioManager.
443  OnDeviceChange();
444}
445
446void AudioOutputController::DoStopDiverting() {
447  DCHECK(message_loop_->BelongsToCurrentThread());
448
449  if (state_ == kClosed)
450    return;
451
452  // Note: OnDeviceChange() will cause the existing stream (the consumer of the
453  // diverted audio data) to be closed, and diverting_to_stream_ will be set
454  // back to NULL.
455  OnDeviceChange();
456  DCHECK(!diverting_to_stream_);
457}
458
459void AudioOutputController::AllowEntryToOnMoreIOData() {
460  DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_));
461  base::AtomicRefCountInc(&num_allowed_io_);
462}
463
464void AudioOutputController::DisallowEntryToOnMoreIOData() {
465  const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_);
466  DCHECK(is_zero);
467}
468
469void AudioOutputController::WedgeCheck() {
470  DCHECK(message_loop_->BelongsToCurrentThread());
471
472  // If we should be playing and we haven't, that's a wedge.
473  if (state_ == kPlaying) {
474    const bool playback_success =
475        base::AtomicRefCountIsOne(&on_more_io_data_called_);
476
477    UMA_HISTOGRAM_BOOLEAN(
478        "Media.AudioOutputControllerPlaybackStartupSuccess", playback_success);
479
480    // Let the AudioManager try and fix it.
481    if (!playback_success)
482      audio_manager_->FixWedgedAudio();
483  }
484}
485
486}  // namespace media
487