Home | History | Annotate | Download | only in audio
      1 // Copyright (c) 2013 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/fake_audio_consumer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/cancelable_callback.h"
     10 #include "base/logging.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/message_loop/message_loop_proxy.h"
     14 #include "base/synchronization/lock.h"
     15 #include "base/threading/thread_checker.h"
     16 #include "base/time/time.h"
     17 #include "media/audio/audio_parameters.h"
     18 #include "media/base/audio_bus.h"
     19 
     20 namespace media {
     21 
     22 class FakeAudioConsumer::Worker
     23     : public base::RefCountedThreadSafe<FakeAudioConsumer::Worker> {
     24  public:
     25   Worker(const scoped_refptr<base::MessageLoopProxy>& worker_loop,
     26          const AudioParameters& params);
     27 
     28   bool IsStopped();
     29   void Start(const ReadCB& read_cb);
     30   void Stop();
     31 
     32  private:
     33   friend class base::RefCountedThreadSafe<Worker>;
     34   ~Worker();
     35 
     36   // Initialize and start regular calls to DoRead() on the worker thread.
     37   void DoStart();
     38 
     39   // Cancel any delayed callbacks to DoRead() in the worker loop's queue.
     40   void DoCancel();
     41 
     42   // Task that regularly calls |read_cb_| according to the playback rate as
     43   // determined by the audio parameters given during construction.  Runs on
     44   // the worker loop.
     45   void DoRead();
     46 
     47   const scoped_refptr<base::MessageLoopProxy> worker_loop_;
     48   const scoped_ptr<AudioBus> audio_bus_;
     49   const base::TimeDelta buffer_duration_;
     50 
     51   base::Lock read_cb_lock_;  // Held while mutating or running |read_cb_|.
     52   ReadCB read_cb_;
     53   base::TimeTicks next_read_time_;
     54 
     55   // Used to cancel any delayed tasks still inside the worker loop's queue.
     56   base::CancelableClosure read_task_cb_;
     57 
     58   base::ThreadChecker thread_checker_;
     59 
     60   DISALLOW_COPY_AND_ASSIGN(Worker);
     61 };
     62 
     63 FakeAudioConsumer::FakeAudioConsumer(
     64     const scoped_refptr<base::MessageLoopProxy>& worker_loop,
     65     const AudioParameters& params)
     66     : worker_(new Worker(worker_loop, params)) {
     67 }
     68 
     69 FakeAudioConsumer::~FakeAudioConsumer() {
     70   DCHECK(worker_->IsStopped());
     71 }
     72 
     73 void FakeAudioConsumer::Start(const ReadCB& read_cb) {
     74   DCHECK(worker_->IsStopped());
     75   worker_->Start(read_cb);
     76 }
     77 
     78 void FakeAudioConsumer::Stop() {
     79   worker_->Stop();
     80 }
     81 
     82 FakeAudioConsumer::Worker::Worker(
     83     const scoped_refptr<base::MessageLoopProxy>& worker_loop,
     84     const AudioParameters& params)
     85     : worker_loop_(worker_loop),
     86       audio_bus_(AudioBus::Create(params)),
     87       buffer_duration_(base::TimeDelta::FromMicroseconds(
     88           params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond /
     89           static_cast<float>(params.sample_rate()))) {
     90   audio_bus_->Zero();
     91 
     92   // Worker can be constructed on any thread, but will DCHECK that its
     93   // Start/Stop methods are called from the same thread.
     94   thread_checker_.DetachFromThread();
     95 }
     96 
     97 FakeAudioConsumer::Worker::~Worker() {
     98   DCHECK(read_cb_.is_null());
     99 }
    100 
    101 bool FakeAudioConsumer::Worker::IsStopped() {
    102   base::AutoLock scoped_lock(read_cb_lock_);
    103   return read_cb_.is_null();
    104 }
    105 
    106 void FakeAudioConsumer::Worker::Start(const ReadCB& read_cb)  {
    107   DCHECK(thread_checker_.CalledOnValidThread());
    108   DCHECK(!read_cb.is_null());
    109   {
    110     base::AutoLock scoped_lock(read_cb_lock_);
    111     DCHECK(read_cb_.is_null());
    112     read_cb_ = read_cb;
    113   }
    114   worker_loop_->PostTask(FROM_HERE, base::Bind(&Worker::DoStart, this));
    115 }
    116 
    117 void FakeAudioConsumer::Worker::DoStart() {
    118   DCHECK(worker_loop_->BelongsToCurrentThread());
    119   next_read_time_ = base::TimeTicks::Now();
    120   read_task_cb_.Reset(base::Bind(&Worker::DoRead, this));
    121   read_task_cb_.callback().Run();
    122 }
    123 
    124 void FakeAudioConsumer::Worker::Stop() {
    125   DCHECK(thread_checker_.CalledOnValidThread());
    126   {
    127     base::AutoLock scoped_lock(read_cb_lock_);
    128     if (read_cb_.is_null())
    129       return;
    130     read_cb_.Reset();
    131   }
    132   worker_loop_->PostTask(FROM_HERE, base::Bind(&Worker::DoCancel, this));
    133 }
    134 
    135 void FakeAudioConsumer::Worker::DoCancel() {
    136   DCHECK(worker_loop_->BelongsToCurrentThread());
    137   read_task_cb_.Cancel();
    138 }
    139 
    140 void FakeAudioConsumer::Worker::DoRead() {
    141   DCHECK(worker_loop_->BelongsToCurrentThread());
    142 
    143   {
    144     base::AutoLock scoped_lock(read_cb_lock_);
    145     if (!read_cb_.is_null())
    146       read_cb_.Run(audio_bus_.get());
    147   }
    148 
    149   // Need to account for time spent here due to the cost of |read_cb_| as well
    150   // as the imprecision of PostDelayedTask().
    151   const base::TimeTicks now = base::TimeTicks::Now();
    152   base::TimeDelta delay = next_read_time_ + buffer_duration_ - now;
    153 
    154   // If we're behind, find the next nearest ontime interval.
    155   if (delay < base::TimeDelta())
    156     delay += buffer_duration_ * (-delay / buffer_duration_ + 1);
    157   next_read_time_ = now + delay;
    158 
    159   worker_loop_->PostDelayedTask(FROM_HERE, read_task_cb_.callback(), delay);
    160 }
    161 
    162 }  // namespace media
    163