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