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_device.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/debug/trace_event.h"
      9 #include "base/threading/thread_restrictions.h"
     10 #include "base/time/time.h"
     11 #include "media/audio/audio_output_controller.h"
     12 #include "media/base/limits.h"
     13 
     14 namespace media {
     15 
     16 // Takes care of invoking the render callback on the audio thread.
     17 // An instance of this class is created for each capture stream in
     18 // OnStreamCreated().
     19 class AudioOutputDevice::AudioThreadCallback
     20     : public AudioDeviceThread::Callback {
     21  public:
     22   AudioThreadCallback(const AudioParameters& audio_parameters,
     23                       base::SharedMemoryHandle memory,
     24                       int memory_length,
     25                       AudioRendererSink::RenderCallback* render_callback);
     26   virtual ~AudioThreadCallback();
     27 
     28   virtual void MapSharedMemory() OVERRIDE;
     29 
     30   // Called whenever we receive notifications about pending data.
     31   virtual void Process(int pending_data) OVERRIDE;
     32 
     33  private:
     34   AudioRendererSink::RenderCallback* render_callback_;
     35   scoped_ptr<AudioBus> output_bus_;
     36   DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback);
     37 };
     38 
     39 AudioOutputDevice::AudioOutputDevice(
     40     scoped_ptr<AudioOutputIPC> ipc,
     41     const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
     42     : ScopedTaskRunnerObserver(io_task_runner),
     43       callback_(NULL),
     44       ipc_(ipc.Pass()),
     45       state_(IDLE),
     46       play_on_start_(true),
     47       session_id_(-1),
     48       stopping_hack_(false) {
     49   CHECK(ipc_);
     50 
     51   // The correctness of the code depends on the relative values assigned in the
     52   // State enum.
     53   COMPILE_ASSERT(IPC_CLOSED < IDLE, invalid_enum_value_assignment_0);
     54   COMPILE_ASSERT(IDLE < CREATING_STREAM, invalid_enum_value_assignment_1);
     55   COMPILE_ASSERT(CREATING_STREAM < PAUSED, invalid_enum_value_assignment_2);
     56   COMPILE_ASSERT(PAUSED < PLAYING, invalid_enum_value_assignment_3);
     57 }
     58 
     59 void AudioOutputDevice::InitializeWithSessionId(const AudioParameters& params,
     60                                                 RenderCallback* callback,
     61                                                 int session_id) {
     62   DCHECK(!callback_) << "Calling InitializeWithSessionId() twice?";
     63   DCHECK(params.IsValid());
     64   audio_parameters_ = params;
     65   callback_ = callback;
     66   session_id_ = session_id;
     67 }
     68 
     69 void AudioOutputDevice::Initialize(const AudioParameters& params,
     70                                    RenderCallback* callback) {
     71   InitializeWithSessionId(params, callback, 0);
     72 }
     73 
     74 AudioOutputDevice::~AudioOutputDevice() {
     75   // The current design requires that the user calls Stop() before deleting
     76   // this class.
     77   DCHECK(audio_thread_.IsStopped());
     78 }
     79 
     80 void AudioOutputDevice::Start() {
     81   DCHECK(callback_) << "Initialize hasn't been called";
     82   task_runner()->PostTask(FROM_HERE,
     83       base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this,
     84                  audio_parameters_));
     85 }
     86 
     87 void AudioOutputDevice::Stop() {
     88   {
     89     base::AutoLock auto_lock(audio_thread_lock_);
     90     audio_thread_.Stop(base::MessageLoop::current());
     91     stopping_hack_ = true;
     92   }
     93 
     94   task_runner()->PostTask(FROM_HERE,
     95       base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this));
     96 }
     97 
     98 void AudioOutputDevice::Play() {
     99   task_runner()->PostTask(FROM_HERE,
    100       base::Bind(&AudioOutputDevice::PlayOnIOThread, this));
    101 }
    102 
    103 void AudioOutputDevice::Pause() {
    104   task_runner()->PostTask(FROM_HERE,
    105       base::Bind(&AudioOutputDevice::PauseOnIOThread, this));
    106 }
    107 
    108 bool AudioOutputDevice::SetVolume(double volume) {
    109   if (volume < 0 || volume > 1.0)
    110     return false;
    111 
    112   if (!task_runner()->PostTask(FROM_HERE,
    113           base::Bind(&AudioOutputDevice::SetVolumeOnIOThread, this, volume))) {
    114     return false;
    115   }
    116 
    117   return true;
    118 }
    119 
    120 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) {
    121   DCHECK(task_runner()->BelongsToCurrentThread());
    122   if (state_ == IDLE) {
    123     state_ = CREATING_STREAM;
    124     ipc_->CreateStream(this, params, session_id_);
    125   }
    126 }
    127 
    128 void AudioOutputDevice::PlayOnIOThread() {
    129   DCHECK(task_runner()->BelongsToCurrentThread());
    130   if (state_ == PAUSED) {
    131     ipc_->PlayStream();
    132     state_ = PLAYING;
    133     play_on_start_ = false;
    134   } else {
    135     play_on_start_ = true;
    136   }
    137 }
    138 
    139 void AudioOutputDevice::PauseOnIOThread() {
    140   DCHECK(task_runner()->BelongsToCurrentThread());
    141   if (state_ == PLAYING) {
    142     ipc_->PauseStream();
    143     state_ = PAUSED;
    144   }
    145   play_on_start_ = false;
    146 }
    147 
    148 void AudioOutputDevice::ShutDownOnIOThread() {
    149   DCHECK(task_runner()->BelongsToCurrentThread());
    150 
    151   // Close the stream, if we haven't already.
    152   if (state_ >= CREATING_STREAM) {
    153     ipc_->CloseStream();
    154     state_ = IDLE;
    155   }
    156 
    157   // We can run into an issue where ShutDownOnIOThread is called right after
    158   // OnStreamCreated is called in cases where Start/Stop are called before we
    159   // get the OnStreamCreated callback.  To handle that corner case, we call
    160   // Stop(). In most cases, the thread will already be stopped.
    161   //
    162   // Another situation is when the IO thread goes away before Stop() is called
    163   // in which case, we cannot use the message loop to close the thread handle
    164   // and can't rely on the main thread existing either.
    165   base::AutoLock auto_lock_(audio_thread_lock_);
    166   base::ThreadRestrictions::ScopedAllowIO allow_io;
    167   audio_thread_.Stop(NULL);
    168   audio_callback_.reset();
    169   stopping_hack_ = false;
    170 }
    171 
    172 void AudioOutputDevice::SetVolumeOnIOThread(double volume) {
    173   DCHECK(task_runner()->BelongsToCurrentThread());
    174   if (state_ >= CREATING_STREAM)
    175     ipc_->SetVolume(volume);
    176 }
    177 
    178 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) {
    179   DCHECK(task_runner()->BelongsToCurrentThread());
    180 
    181   // Do nothing if the stream has been closed.
    182   if (state_ < CREATING_STREAM)
    183     return;
    184 
    185   // TODO(miu): Clean-up inconsistent and incomplete handling here.
    186   // http://crbug.com/180640
    187   switch (state) {
    188     case AudioOutputIPCDelegate::kPlaying:
    189     case AudioOutputIPCDelegate::kPaused:
    190       break;
    191     case AudioOutputIPCDelegate::kError:
    192       DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)";
    193       // Don't dereference the callback object if the audio thread
    194       // is stopped or stopping.  That could mean that the callback
    195       // object has been deleted.
    196       // TODO(tommi): Add an explicit contract for clearing the callback
    197       // object.  Possibly require calling Initialize again or provide
    198       // a callback object via Start() and clear it in Stop().
    199       if (!audio_thread_.IsStopped())
    200         callback_->OnRenderError();
    201       break;
    202     default:
    203       NOTREACHED();
    204       break;
    205   }
    206 }
    207 
    208 void AudioOutputDevice::OnStreamCreated(
    209     base::SharedMemoryHandle handle,
    210     base::SyncSocket::Handle socket_handle,
    211     int length) {
    212   DCHECK(task_runner()->BelongsToCurrentThread());
    213 #if defined(OS_WIN)
    214   DCHECK(handle);
    215   DCHECK(socket_handle);
    216 #else
    217   DCHECK_GE(handle.fd, 0);
    218   DCHECK_GE(socket_handle, 0);
    219 #endif
    220   DCHECK_GT(length, 0);
    221 
    222   if (state_ != CREATING_STREAM)
    223     return;
    224 
    225   // We can receive OnStreamCreated() on the IO thread after the client has
    226   // called Stop() but before ShutDownOnIOThread() is processed. In such a
    227   // situation |callback_| might point to freed memory. Instead of starting
    228   // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called.
    229   //
    230   // TODO(scherkus): The real fix is to have sane ownership semantics. The fact
    231   // that |callback_| (which should own and outlive this object!) can point to
    232   // freed memory is a mess. AudioRendererSink should be non-refcounted so that
    233   // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and
    234   // delete as they see fit. AudioOutputDevice should internally use WeakPtr
    235   // to handle teardown and thread hopping. See http://crbug.com/151051 for
    236   // details.
    237   base::AutoLock auto_lock(audio_thread_lock_);
    238   if (stopping_hack_)
    239     return;
    240 
    241   DCHECK(audio_thread_.IsStopped());
    242   audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback(
    243       audio_parameters_, handle, length, callback_));
    244   audio_thread_.Start(
    245       audio_callback_.get(), socket_handle, "AudioOutputDevice", true);
    246   state_ = PAUSED;
    247 
    248   // We handle the case where Play() and/or Pause() may have been called
    249   // multiple times before OnStreamCreated() gets called.
    250   if (play_on_start_)
    251     PlayOnIOThread();
    252 }
    253 
    254 void AudioOutputDevice::OnIPCClosed() {
    255   DCHECK(task_runner()->BelongsToCurrentThread());
    256   state_ = IPC_CLOSED;
    257   ipc_.reset();
    258 }
    259 
    260 void AudioOutputDevice::WillDestroyCurrentMessageLoop() {
    261   LOG(ERROR) << "IO loop going away before the audio device has been stopped";
    262   ShutDownOnIOThread();
    263 }
    264 
    265 // AudioOutputDevice::AudioThreadCallback
    266 
    267 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback(
    268     const AudioParameters& audio_parameters,
    269     base::SharedMemoryHandle memory,
    270     int memory_length,
    271     AudioRendererSink::RenderCallback* render_callback)
    272     : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1),
    273       render_callback_(render_callback) {}
    274 
    275 AudioOutputDevice::AudioThreadCallback::~AudioThreadCallback() {
    276 }
    277 
    278 void AudioOutputDevice::AudioThreadCallback::MapSharedMemory() {
    279   CHECK_EQ(total_segments_, 1);
    280   CHECK(shared_memory_.Map(memory_length_));
    281   DCHECK_EQ(memory_length_, AudioBus::CalculateMemorySize(audio_parameters_));
    282 
    283   output_bus_ =
    284       AudioBus::WrapMemory(audio_parameters_, shared_memory_.memory());
    285 }
    286 
    287 // Called whenever we receive notifications about pending data.
    288 void AudioOutputDevice::AudioThreadCallback::Process(int pending_data) {
    289   // Negative |pending_data| indicates the browser side stream has stopped.
    290   if (pending_data < 0)
    291     return;
    292 
    293   // Convert the number of pending bytes in the render buffer into milliseconds.
    294   int audio_delay_milliseconds = pending_data / bytes_per_ms_;
    295 
    296   TRACE_EVENT0("audio", "AudioOutputDevice::FireRenderCallback");
    297 
    298   // Update the audio-delay measurement then ask client to render audio.  Since
    299   // |output_bus_| is wrapping the shared memory the Render() call is writing
    300   // directly into the shared memory.
    301   render_callback_->Render(output_bus_.get(), audio_delay_milliseconds);
    302 }
    303 
    304 }  // namespace media.
    305