Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / media / audio / fake_audio_consumer.cc
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/location.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/single_thread_task_runner.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::SingleThreadTaskRunner>& worker_task_runner,
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::SingleThreadTaskRunner> worker_task_runner_;
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::SingleThreadTaskRunner>& worker_task_runner,
65     const AudioParameters& params)
66     : worker_(new Worker(worker_task_runner, 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::SingleThreadTaskRunner>& worker_task_runner,
84     const AudioParameters& params)
85     : worker_task_runner_(worker_task_runner),
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_task_runner_->PostTask(FROM_HERE, base::Bind(&Worker::DoStart, this));
115 }
116
117 void FakeAudioConsumer::Worker::DoStart() {
118   DCHECK(worker_task_runner_->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_task_runner_->PostTask(FROM_HERE, base::Bind(&Worker::DoCancel, this));
133 }
134
135 void FakeAudioConsumer::Worker::DoCancel() {
136   DCHECK(worker_task_runner_->BelongsToCurrentThread());
137   read_task_cb_.Cancel();
138 }
139
140 void FakeAudioConsumer::Worker::DoRead() {
141   DCHECK(worker_task_runner_->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_task_runner_->PostDelayedTask(
160       FROM_HERE, read_task_cb_.callback(), delay);
161 }
162
163 }  // namespace media