Home | History | Annotate | Download | only in audio
      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 
     16 using base::TimeDelta;
     17 
     18 namespace 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.
     23 static const int kPowerMeasurementTimeConstantMillis = 10;
     24 #endif
     25 
     26 AudioOutputController::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 
     54 AudioOutputController::~AudioOutputController() {
     55   DCHECK_EQ(kClosed, state_);
     56 }
     57 
     58 // static
     59 scoped_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 
     78 void AudioOutputController::Play() {
     79   message_loop_->PostTask(FROM_HERE, base::Bind(
     80       &AudioOutputController::DoPlay, this));
     81 }
     82 
     83 void AudioOutputController::Pause() {
     84   message_loop_->PostTask(FROM_HERE, base::Bind(
     85       &AudioOutputController::DoPause, this));
     86 }
     87 
     88 void 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 
     94 void AudioOutputController::SetVolume(double volume) {
     95   message_loop_->PostTask(FROM_HERE, base::Bind(
     96       &AudioOutputController::DoSetVolume, this, volume));
     97 }
     98 
     99 void 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 
    108 void 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 
    117 void 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 
    161 void 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 
    196 void 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 
    213 void 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(-1);
    227 
    228   handler_->OnPaused();
    229 }
    230 
    231 void 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 
    243 void 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 
    261 std::string AudioOutputController::DoGetOutputDeviceId() const {
    262   DCHECK(message_loop_->BelongsToCurrentThread());
    263   return output_device_id_;
    264 }
    265 
    266 void 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 
    286 void AudioOutputController::DoReportError() {
    287   DCHECK(message_loop_->BelongsToCurrentThread());
    288   if (state_ != kClosed)
    289     handler_->OnError();
    290 }
    291 
    292 int 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 
    316 void 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 
    322 void 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 
    342 void 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 
    372 const AudioParameters& AudioOutputController::GetAudioParameters() {
    373   return params_;
    374 }
    375 
    376 void AudioOutputController::StartDiverting(AudioOutputStream* to_stream) {
    377   message_loop_->PostTask(
    378       FROM_HERE,
    379       base::Bind(&AudioOutputController::DoStartDiverting, this, to_stream));
    380 }
    381 
    382 void AudioOutputController::StopDiverting() {
    383   message_loop_->PostTask(
    384       FROM_HERE, base::Bind(&AudioOutputController::DoStopDiverting, this));
    385 }
    386 
    387 void 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 
    401 void 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 
    414 std::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 
    423 void 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