12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "media/audio/fake_audio_consumer.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind_helpers.h"
9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/cancelable_callback.h"
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/location.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/memory/scoped_ptr.h"
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/single_thread_task_runner.h"
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/synchronization/lock.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/threading/thread_checker.h"
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "media/audio/audio_parameters.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "media/base/audio_bus.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace media {
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochclass FakeAudioConsumer::Worker
23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    : public base::RefCountedThreadSafe<FakeAudioConsumer::Worker> {
24eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch public:
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Worker(const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner,
26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch         const AudioParameters& params);
27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  bool IsStopped();
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void Start(const ReadCB& read_cb);
30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void Stop();
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch private:
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  friend class base::RefCountedThreadSafe<Worker>;
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ~Worker();
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Initialize and start regular calls to DoRead() on the worker thread.
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void DoStart();
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Cancel any delayed callbacks to DoRead() in the worker loop's queue.
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void DoCancel();
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Task that regularly calls |read_cb_| according to the playback rate as
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // determined by the audio parameters given during construction.  Runs on
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // the worker loop.
45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void DoRead();
46eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner_;
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const scoped_ptr<AudioBus> audio_bus_;
49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const base::TimeDelta buffer_duration_;
50eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
51eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::Lock read_cb_lock_;  // Held while mutating or running |read_cb_|.
52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ReadCB read_cb_;
53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::TimeTicks next_read_time_;
54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Used to cancel any delayed tasks still inside the worker loop's queue.
56eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::CancelableClosure read_task_cb_;
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ThreadChecker thread_checker_;
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DISALLOW_COPY_AND_ASSIGN(Worker);
61eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch};
62eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)FakeAudioConsumer::FakeAudioConsumer(
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner,
65eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const AudioParameters& params)
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : worker_(new Worker(worker_task_runner, params)) {
67eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
68eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochFakeAudioConsumer::~FakeAudioConsumer() {
70eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(worker_->IsStopped());
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid FakeAudioConsumer::Start(const ReadCB& read_cb) {
74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(worker_->IsStopped());
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  worker_->Start(read_cb);
76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
77eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid FakeAudioConsumer::Stop() {
79eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  worker_->Stop();
80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochFakeAudioConsumer::Worker::Worker(
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner,
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const AudioParameters& params)
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : worker_task_runner_(worker_task_runner),
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      audio_bus_(AudioBus::Create(params)),
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      buffer_duration_(base::TimeDelta::FromMicroseconds(
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond /
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          static_cast<float>(params.sample_rate()))) {
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  audio_bus_->Zero();
91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
92eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Worker can be constructed on any thread, but will DCHECK that its
93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Start/Stop methods are called from the same thread.
94eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  thread_checker_.DetachFromThread();
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
97eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochFakeAudioConsumer::Worker::~Worker() {
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(read_cb_.is_null());
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool FakeAudioConsumer::Worker::IsStopped() {
102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::AutoLock scoped_lock(read_cb_lock_);
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return read_cb_.is_null();
104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
105eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
106eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid FakeAudioConsumer::Worker::Start(const ReadCB& read_cb)  {
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(thread_checker_.CalledOnValidThread());
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(!read_cb.is_null());
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  {
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    base::AutoLock scoped_lock(read_cb_lock_);
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    DCHECK(read_cb_.is_null());
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    read_cb_ = read_cb;
113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  worker_task_runner_->PostTask(FROM_HERE, base::Bind(&Worker::DoStart, this));
115eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
117eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid FakeAudioConsumer::Worker::DoStart() {
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(worker_task_runner_->BelongsToCurrentThread());
1197d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  next_read_time_ = base::TimeTicks::Now();
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  read_task_cb_.Reset(base::Bind(&Worker::DoRead, this));
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  read_task_cb_.callback().Run();
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid FakeAudioConsumer::Worker::Stop() {
125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(thread_checker_.CalledOnValidThread());
126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  {
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    base::AutoLock scoped_lock(read_cb_lock_);
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (read_cb_.is_null())
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return;
130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    read_cb_.Reset();
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  worker_task_runner_->PostTask(FROM_HERE, base::Bind(&Worker::DoCancel, this));
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid FakeAudioConsumer::Worker::DoCancel() {
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(worker_task_runner_->BelongsToCurrentThread());
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  read_task_cb_.Cancel();
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid FakeAudioConsumer::Worker::DoRead() {
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(worker_task_runner_->BelongsToCurrentThread());
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  {
144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    base::AutoLock scoped_lock(read_cb_lock_);
145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (!read_cb_.is_null())
146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      read_cb_.Run(audio_bus_.get());
147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Need to account for time spent here due to the cost of |read_cb_| as well
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // as the imprecision of PostDelayedTask().
1517d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::TimeTicks now = base::TimeTicks::Now();
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::TimeDelta delay = next_read_time_ + buffer_duration_ - now;
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If we're behind, find the next nearest ontime interval.
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (delay < base::TimeDelta())
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    delay += buffer_duration_ * (-delay / buffer_duration_ + 1);
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  next_read_time_ = now + delay;
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  worker_task_runner_->PostDelayedTask(
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FROM_HERE, read_task_cb_.callback(), delay);
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace media
164