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