audio_output_controller.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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      number_polling_attempts_left_(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
136  // Close() can be called before DoCreate() is executed.
137  if (state_ == kClosed)
138    return;
139
140  DoStopCloseAndClearStream();  // Calls RemoveOutputDeviceChangeListener().
141  DCHECK_EQ(kEmpty, state_);
142
143  stream_ = diverting_to_stream_ ?
144      diverting_to_stream_ :
145      audio_manager_->MakeAudioOutputStreamProxy(params_, output_device_id_,
146                                                 input_device_id_);
147  if (!stream_) {
148    state_ = kError;
149    handler_->OnError();
150    return;
151  }
152
153  if (!stream_->Open()) {
154    DoStopCloseAndClearStream();
155    state_ = kError;
156    handler_->OnError();
157    return;
158  }
159
160  // Everything started okay, so re-register for state change callbacks if
161  // stream_ was created via AudioManager.
162  if (stream_ != diverting_to_stream_)
163    audio_manager_->AddOutputDeviceChangeListener(this);
164
165  // We have successfully opened the stream. Set the initial volume.
166  stream_->SetVolume(volume_);
167
168  // Finally set the state to kCreated.
169  state_ = kCreated;
170
171  // And then report we have been created if we haven't done so already.
172  if (!is_for_device_change)
173    handler_->OnCreated();
174}
175
176void AudioOutputController::DoPlay() {
177  DCHECK(message_loop_->BelongsToCurrentThread());
178  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime");
179
180  // We can start from created or paused state.
181  if (state_ != kCreated && state_ != kPaused)
182    return;
183
184  // Ask for first packet.
185  sync_reader_->UpdatePendingBytes(0);
186
187  state_ = kPlaying;
188
189#if defined(AUDIO_POWER_MONITORING)
190  power_monitor_.Reset();
191  power_poll_callback_.Reset(
192      base::Bind(&AudioOutputController::ReportPowerMeasurementPeriodically,
193                 this));
194  // Run the callback to send an initial notification that we're starting in
195  // silence, and to schedule periodic callbacks.
196  power_poll_callback_.callback().Run();
197#endif
198
199  // We start the AudioOutputStream lazily.
200  AllowEntryToOnMoreIOData();
201  stream_->Start(this);
202
203  handler_->OnPlaying();
204}
205
206#if defined(AUDIO_POWER_MONITORING)
207void AudioOutputController::ReportPowerMeasurementPeriodically() {
208  DCHECK(message_loop_->BelongsToCurrentThread());
209  const std::pair<float, bool>& reading =
210      power_monitor_.ReadCurrentPowerAndClip();
211  handler_->OnPowerMeasured(reading.first, reading.second);
212  message_loop_->PostDelayedTask(
213      FROM_HERE, power_poll_callback_.callback(),
214      TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond);
215}
216#endif
217
218void AudioOutputController::StopStream() {
219  DCHECK(message_loop_->BelongsToCurrentThread());
220
221  if (state_ == kPlaying) {
222    stream_->Stop();
223    DisallowEntryToOnMoreIOData();
224
225#if defined(AUDIO_POWER_MONITORING)
226    power_poll_callback_.Cancel();
227#endif
228
229    state_ = kPaused;
230  }
231}
232
233void AudioOutputController::DoPause() {
234  DCHECK(message_loop_->BelongsToCurrentThread());
235  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime");
236
237  StopStream();
238
239  if (state_ != kPaused)
240    return;
241
242  // Let the renderer know we've stopped.  Necessary to let PPAPI clients know
243  // audio has been shutdown.  TODO(dalecurtis): This stinks.  PPAPI should have
244  // a better way to know when it should exit PPB_Audio_Shared::Run().
245  sync_reader_->UpdatePendingBytes(-1);
246
247#if defined(AUDIO_POWER_MONITORING)
248  // Paused means silence follows.
249  handler_->OnPowerMeasured(AudioPowerMonitor::zero_power(), false);
250#endif
251
252  handler_->OnPaused();
253}
254
255void AudioOutputController::DoClose() {
256  DCHECK(message_loop_->BelongsToCurrentThread());
257  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime");
258
259  if (state_ != kClosed) {
260    DoStopCloseAndClearStream();
261    sync_reader_->Close();
262    state_ = kClosed;
263  }
264}
265
266void AudioOutputController::DoSetVolume(double volume) {
267  DCHECK(message_loop_->BelongsToCurrentThread());
268
269  // Saves the volume to a member first. We may not be able to set the volume
270  // right away but when the stream is created we'll set the volume.
271  volume_ = volume;
272
273  switch (state_) {
274    case kCreated:
275    case kPlaying:
276    case kPaused:
277      stream_->SetVolume(volume_);
278      break;
279    default:
280      return;
281  }
282}
283
284std::string AudioOutputController::DoGetOutputDeviceId() const {
285  DCHECK(message_loop_->BelongsToCurrentThread());
286  return output_device_id_;
287}
288
289void AudioOutputController::DoSwitchOutputDevice(
290    const std::string& output_device_id) {
291  DCHECK(message_loop_->BelongsToCurrentThread());
292
293  if (state_ == kClosed)
294    return;
295
296  output_device_id_ = output_device_id;
297
298  // If output is currently diverted, we must not call OnDeviceChange
299  // since it would break the diverted setup. Once diversion is
300  // finished using StopDiverting() the output will switch to the new
301  // device ID.
302  if (stream_ != diverting_to_stream_)
303    OnDeviceChange();
304}
305
306void AudioOutputController::DoReportError() {
307  DCHECK(message_loop_->BelongsToCurrentThread());
308  if (state_ != kClosed)
309    handler_->OnError();
310}
311
312int AudioOutputController::OnMoreData(AudioBus* dest,
313                                      AudioBuffersState buffers_state) {
314  return OnMoreIOData(NULL, dest, buffers_state);
315}
316
317int AudioOutputController::OnMoreIOData(AudioBus* source,
318                                        AudioBus* dest,
319                                        AudioBuffersState buffers_state) {
320  DisallowEntryToOnMoreIOData();
321  TRACE_EVENT0("audio", "AudioOutputController::OnMoreIOData");
322
323  sync_reader_->Read(source, dest);
324
325  const int frames = dest->frames();
326  sync_reader_->UpdatePendingBytes(
327      buffers_state.total_bytes() + frames * params_.GetBytesPerFrame());
328
329#if defined(AUDIO_POWER_MONITORING)
330  power_monitor_.Scan(*dest, frames);
331#endif
332
333  AllowEntryToOnMoreIOData();
334  return frames;
335}
336
337void AudioOutputController::OnError(AudioOutputStream* stream) {
338  // Handle error on the audio controller thread.
339  message_loop_->PostTask(FROM_HERE, base::Bind(
340      &AudioOutputController::DoReportError, this));
341}
342
343void AudioOutputController::DoStopCloseAndClearStream() {
344  DCHECK(message_loop_->BelongsToCurrentThread());
345
346  // Allow calling unconditionally and bail if we don't have a stream_ to close.
347  if (stream_) {
348    // De-register from state change callbacks if stream_ was created via
349    // AudioManager.
350    if (stream_ != diverting_to_stream_)
351      audio_manager_->RemoveOutputDeviceChangeListener(this);
352
353    StopStream();
354    stream_->Close();
355    if (stream_ == diverting_to_stream_)
356      diverting_to_stream_ = NULL;
357    stream_ = NULL;
358  }
359
360  state_ = kEmpty;
361}
362
363void AudioOutputController::OnDeviceChange() {
364  DCHECK(message_loop_->BelongsToCurrentThread());
365  SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime");
366
367  // TODO(dalecurtis): Notify the renderer side that a device change has
368  // occurred.  Currently querying the hardware information here will lead to
369  // crashes on OSX.  See http://crbug.com/158170.
370
371  // Recreate the stream (DoCreate() will first shut down an existing stream).
372  // Exit if we ran into an error.
373  const State original_state = state_;
374  DoCreate(true);
375  if (!stream_ || state_ == kError)
376    return;
377
378  // Get us back to the original state or an equivalent state.
379  switch (original_state) {
380    case kPlaying:
381      DoPlay();
382      return;
383    case kCreated:
384    case kPaused:
385      // From the outside these two states are equivalent.
386      return;
387    default:
388      NOTREACHED() << "Invalid original state.";
389  }
390}
391
392const AudioParameters& AudioOutputController::GetAudioParameters() {
393  return params_;
394}
395
396void AudioOutputController::StartDiverting(AudioOutputStream* to_stream) {
397  message_loop_->PostTask(
398      FROM_HERE,
399      base::Bind(&AudioOutputController::DoStartDiverting, this, to_stream));
400}
401
402void AudioOutputController::StopDiverting() {
403  message_loop_->PostTask(
404      FROM_HERE, base::Bind(&AudioOutputController::DoStopDiverting, this));
405}
406
407void AudioOutputController::DoStartDiverting(AudioOutputStream* to_stream) {
408  DCHECK(message_loop_->BelongsToCurrentThread());
409
410  if (state_ == kClosed)
411    return;
412
413  DCHECK(!diverting_to_stream_);
414  diverting_to_stream_ = to_stream;
415  // Note: OnDeviceChange() will engage the "re-create" process, which will
416  // detect and use the alternate AudioOutputStream rather than create a new one
417  // via AudioManager.
418  OnDeviceChange();
419}
420
421void AudioOutputController::DoStopDiverting() {
422  DCHECK(message_loop_->BelongsToCurrentThread());
423
424  if (state_ == kClosed)
425    return;
426
427  // Note: OnDeviceChange() will cause the existing stream (the consumer of the
428  // diverted audio data) to be closed, and diverting_to_stream_ will be set
429  // back to NULL.
430  OnDeviceChange();
431  DCHECK(!diverting_to_stream_);
432}
433
434void AudioOutputController::AllowEntryToOnMoreIOData() {
435  DCHECK(base::AtomicRefCountIsZero(&num_allowed_io_));
436  base::AtomicRefCountInc(&num_allowed_io_);
437}
438
439void AudioOutputController::DisallowEntryToOnMoreIOData() {
440  const bool is_zero = !base::AtomicRefCountDec(&num_allowed_io_);
441  DCHECK(is_zero);
442}
443
444}  // namespace media
445