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