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