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