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