Chromium Code Reviews| Index: media/audio/fake_audio_provider.cc |
| diff --git a/media/audio/fake_audio_input_stream.cc b/media/audio/fake_audio_provider.cc |
| similarity index 52% |
| copy from media/audio/fake_audio_input_stream.cc |
| copy to media/audio/fake_audio_provider.cc |
| index 4632b258eda2abaf261bb7dba214ab6aba15d8bb..342056a751131d7f4dac6552ccbded42394e0d7e 100644 |
| --- a/media/audio/fake_audio_input_stream.cc |
| +++ b/media/audio/fake_audio_provider.cc |
| @@ -1,19 +1,23 @@ |
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| -#include "media/audio/fake_audio_input_stream.h" |
| +#include "media/audio/fake_audio_provider.h" |
| #include "base/bind.h" |
| -#include "base/command_line.h" |
| +#include "base/cancelable_callback.h" |
| #include "base/files/file.h" |
| +#include "base/files/file_path.h" |
| #include "base/lazy_instance.h" |
| -#include "media/audio/audio_manager_base.h" |
| +#include "base/location.h" |
| +#include "base/single_thread_task_runner.h" |
| +#include "base/synchronization/lock.h" |
| +#include "base/threading/thread_checker.h" |
| +#include "base/time/time.h" |
| +#include "media/audio/audio_parameters.h" |
| +#include "media/audio/sounds/wav_audio_handler.h" |
| #include "media/base/audio_bus.h" |
| -#include "media/base/media_switches.h" |
| - |
| -using base::TimeTicks; |
| -using base::TimeDelta; |
| +#include "media/base/audio_converter.h" |
| namespace media { |
| @@ -102,21 +106,96 @@ static base::LazyInstance<BeepContext> g_beep_context = |
| } // namespace |
| -AudioInputStream* FakeAudioInputStream::MakeFakeStream( |
| - AudioManagerBase* manager, |
| - const AudioParameters& params) { |
| - return new FakeAudioInputStream(manager, params); |
| +class FakeAudioProvider::Worker |
| + : public base::RefCountedThreadSafe<FakeAudioProvider::Worker>, |
| + public AudioConverter::InputCallback { |
| + public: |
| + Worker(const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner, |
| + const AudioParameters& params); |
| + |
| + void Open(const base::FilePath& optional_path_to_wav_file); |
| + void Start(InputCB input_cb); |
| + void Stop(); |
| + bool IsStopped(); |
| + |
| + private: |
| + friend class base::RefCountedThreadSafe<Worker>; |
| + ~Worker() override; |
| + |
| + void DoCallback(); |
| + void DoOpen(const base::FilePath& optional_path_to_wav_file); |
| + void DoStart(); |
| + void DoCancel(); |
| + |
| + void LoadWavFileIntoWorker(const base::FilePath& wav_filename); |
|
DaleCurtis
2015/02/17 23:27:49
The worker is already a pretty complicated beast.
phoglund_chromium
2015/02/18 10:22:05
All the data needs to live on the worker's thread,
|
| + |
| + // Returns true if the device is playing from a file; false if we're beeping. |
| + bool PlayingFromFile(); |
| + |
| + void PlayFile(); |
| + void PlayBeep(); |
| + |
| + base::Lock input_cb_lock_; // Held while mutating or running |input_cb_|. |
| + InputCB input_cb_; |
| + base::CancelableClosure input_task_cb_; |
| + scoped_ptr<uint8[]> buffer_; |
| + int buffer_size_; |
| + AudioParameters params_; |
| + const scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner_; |
| + base::TimeTicks last_callback_time_; |
| + base::TimeDelta callback_interval_; |
| + base::TimeDelta interval_from_last_beep_; |
| + int beep_duration_in_buffers_; |
| + int beep_generated_in_buffers_; |
| + int beep_period_in_frames_; |
| + scoped_ptr<media::AudioBus> audio_bus_; |
| + scoped_ptr<uint8[]> wav_file_data_; |
| + scoped_ptr<media::WavAudioHandler> wav_audio_handler_; |
| + scoped_ptr<media::AudioConverter> file_audio_converter_; |
| + int wav_file_read_pos_; |
| + |
| + base::ThreadChecker thread_checker_; |
| + |
| + // If running in file mode, this provides audio data from wav_audio_handler_. |
| + double ProvideInput(AudioBus* audio_bus, |
| + base::TimeDelta buffer_delay) override; |
| +}; |
| + |
| +FakeAudioProvider::FakeAudioProvider( |
| + const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner, |
| + const AudioParameters& params) |
| + : worker_(new Worker(worker_task_runner, params)) { |
| +} |
| + |
| +FakeAudioProvider::~FakeAudioProvider() { |
| + DCHECK(worker_->IsStopped()); |
| } |
| -FakeAudioInputStream::FakeAudioInputStream(AudioManagerBase* manager, |
| - const AudioParameters& params) |
| - : audio_manager_(manager), |
| - callback_(NULL), |
| - buffer_size_((params.channels() * params.bits_per_sample() * |
| +void FakeAudioProvider::OpenInBeepMode() { |
| + worker_->Open(base::FilePath()); |
| +} |
| + |
| +void FakeAudioProvider::OpenInFileMode(const base::FilePath& path_to_wav_file) { |
| + worker_->Open(path_to_wav_file); |
| +} |
| + |
| +void FakeAudioProvider::Start(const InputCB& input_cb) { |
| + DCHECK(worker_->IsStopped()); |
| + worker_->Start(input_cb); |
| +} |
| + |
| +void FakeAudioProvider::Stop() { |
| + worker_->Stop(); |
| +} |
| + |
| +FakeAudioProvider::Worker::Worker( |
| + const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner, |
| + const AudioParameters& params) |
| + : buffer_size_((params.channels() * params.bits_per_sample() * |
| params.frames_per_buffer()) / |
| 8), |
| params_(params), |
| - task_runner_(manager->GetTaskRunner()), |
| + worker_task_runner_(worker_task_runner), |
| callback_interval_(base::TimeDelta::FromMilliseconds( |
| (params.frames_per_buffer() * 1000) / params.sample_rate())), |
| beep_duration_in_buffers_(kBeepDurationMilliseconds * |
| @@ -126,70 +205,37 @@ FakeAudioInputStream::FakeAudioInputStream(AudioManagerBase* manager, |
| beep_generated_in_buffers_(0), |
| beep_period_in_frames_(params.sample_rate() / kBeepFrequency), |
| audio_bus_(AudioBus::Create(params)), |
| - wav_file_read_pos_(0), |
| - weak_factory_(this) { |
| - DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| + wav_file_read_pos_(0) { |
| + // Ensure Start, Stop and Open is called on the same thread (where we're |
| + // created doesn't matter though). |
| + thread_checker_.DetachFromThread(); |
| } |
| -FakeAudioInputStream::~FakeAudioInputStream() {} |
| - |
| -bool FakeAudioInputStream::Open() { |
| - DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| - buffer_.reset(new uint8[buffer_size_]); |
| - memset(buffer_.get(), 0, buffer_size_); |
| - audio_bus_->Zero(); |
| - |
| - if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| - switches::kUseFileForFakeAudioCapture)) { |
| - OpenInFileMode(base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( |
| - switches::kUseFileForFakeAudioCapture)); |
| - } |
| - |
| - return true; |
| +FakeAudioProvider::Worker::~Worker() { |
| + DCHECK(input_cb_.is_null()); |
| } |
| -void FakeAudioInputStream::Start(AudioInputCallback* callback) { |
| - DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| - DCHECK(!callback_); |
| - callback_ = callback; |
| - last_callback_time_ = TimeTicks::Now(); |
| - |
| - task_runner_->PostDelayedTask( |
| - FROM_HERE, |
| - base::Bind(&FakeAudioInputStream::DoCallback, weak_factory_.GetWeakPtr()), |
| - callback_interval_); |
| +void FakeAudioProvider::Worker::Open( |
| + const base::FilePath& optional_path_to_wav_file) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + worker_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&Worker::DoOpen, this, optional_path_to_wav_file)); |
| } |
| -void FakeAudioInputStream::DoCallback() { |
| - DCHECK(callback_); |
| - |
| - const TimeTicks now = TimeTicks::Now(); |
| - base::TimeDelta next_callback_time = |
| - last_callback_time_ + callback_interval_ * 2 - now; |
| - |
| - // If we are falling behind, try to catch up as much as we can in the next |
| - // callback. |
| - if (next_callback_time < base::TimeDelta()) |
| - next_callback_time = base::TimeDelta(); |
| - |
| - if (PlayingFromFile()) { |
| - PlayFile(); |
| - } else { |
| - PlayBeep(); |
| - } |
| - |
| - last_callback_time_ = now; |
| +void FakeAudioProvider::Worker::DoOpen( |
| + const base::FilePath& optional_path_to_wav_file) { |
| + DCHECK(worker_task_runner_->BelongsToCurrentThread()); |
| + buffer_.reset(new uint8[buffer_size_]); |
| + memset(buffer_.get(), 0, buffer_size_); |
| + audio_bus_->Zero(); |
| - task_runner_->PostDelayedTask( |
| - FROM_HERE, |
| - base::Bind(&FakeAudioInputStream::DoCallback, weak_factory_.GetWeakPtr()), |
| - next_callback_time); |
| + if (!optional_path_to_wav_file.empty()) |
| + LoadWavFileIntoWorker(optional_path_to_wav_file); |
| } |
| -void FakeAudioInputStream::OpenInFileMode(const base::FilePath& wav_filename) { |
| - CHECK(!wav_filename.empty()) |
| - << "You must pass the file to use as argument to --" |
| - << switches::kUseFileForFakeAudioCapture << "."; |
| +void FakeAudioProvider::Worker::LoadWavFileIntoWorker( |
| + const base::FilePath& wav_filename) { |
| + DCHECK(worker_task_runner_->BelongsToCurrentThread()); |
| // Read the file, and put its data in a scoped_ptr so it gets deleted later. |
| size_t file_length = 0; |
| @@ -212,22 +258,73 @@ void FakeAudioInputStream::OpenInFileMode(const base::FilePath& wav_filename) { |
| file_audio_converter_->AddInput(this); |
| } |
| -bool FakeAudioInputStream::PlayingFromFile() { |
| +void FakeAudioProvider::Worker::Start(InputCB callback) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + { |
| + base::AutoLock scoped_lock(input_cb_lock_); |
| + DCHECK(input_cb_.is_null()); |
| + input_cb_ = callback; |
| + } |
| + worker_task_runner_->PostTask(FROM_HERE, base::Bind(&Worker::DoStart, this)); |
| +} |
| + |
| +void FakeAudioProvider::Worker::DoStart() { |
| + DCHECK(worker_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(buffer_.get() != nullptr) << "Must be opened first."; |
| + |
| + last_callback_time_ = base::TimeTicks::Now(); |
| + |
| + input_task_cb_.Reset(base::Bind(&Worker::DoCallback, this)); |
| + input_task_cb_.callback().Run(); |
| +} |
| + |
| +void FakeAudioProvider::Worker::DoCallback() { |
| + DCHECK(worker_task_runner_->BelongsToCurrentThread()); |
| + |
| + const base::TimeTicks now = base::TimeTicks::Now(); |
| + base::TimeDelta delay = last_callback_time_ + callback_interval_ * 2 - now; |
| + |
| + // If we are falling behind, try to catch up as much as we can in the next |
| + // callback. |
| + if (delay < base::TimeDelta()) |
| + delay = base::TimeDelta(); |
| + |
| + if (PlayingFromFile()) { |
| + PlayFile(); |
| + } else { |
| + PlayBeep(); |
| + } |
| + |
| + last_callback_time_ = now; |
| + worker_task_runner_->PostDelayedTask(FROM_HERE, input_task_cb_.callback(), |
| + delay); |
| +} |
| + |
| +bool FakeAudioProvider::Worker::PlayingFromFile() { |
| return wav_audio_handler_.get() != nullptr; |
| } |
| -void FakeAudioInputStream::PlayFile() { |
| +void FakeAudioProvider::Worker::PlayFile() { |
| + DCHECK(worker_task_runner_->BelongsToCurrentThread()); |
| + |
| // Stop playing if we've played out the whole file. |
| if (wav_audio_handler_->AtEnd(wav_file_read_pos_)) |
| return; |
| file_audio_converter_->Convert(audio_bus_.get()); |
| - callback_->OnData(this, audio_bus_.get(), buffer_size_, 1.0); |
| + { |
| + base::AutoLock scoped_lock(input_cb_lock_); |
| + if (input_cb_.is_null()) |
| + return; |
| + input_cb_.Run(audio_bus_.get(), buffer_size_); |
| + } |
| } |
| -void FakeAudioInputStream::PlayBeep() { |
| +void FakeAudioProvider::Worker::PlayBeep() { |
| + DCHECK(worker_task_runner_->BelongsToCurrentThread()); |
| + |
| // Accumulate the time from the last beep. |
| - interval_from_last_beep_ += TimeTicks::Now() - last_callback_time_; |
| + interval_from_last_beep_ += base::TimeTicks::Now() - last_callback_time_; |
| memset(buffer_.get(), 0, buffer_size_); |
| bool should_beep = false; |
| @@ -235,7 +332,7 @@ void FakeAudioInputStream::PlayBeep() { |
| BeepContext* beep_context = g_beep_context.Pointer(); |
| if (beep_context->automatic_beep()) { |
| base::TimeDelta delta = interval_from_last_beep_ - |
| - TimeDelta::FromMilliseconds(kAutomaticBeepIntervalInMs); |
| + base::TimeDelta::FromMilliseconds(kAutomaticBeepIntervalInMs); |
| if (delta > base::TimeDelta()) { |
| should_beep = true; |
| interval_from_last_beep_ = delta; |
| @@ -272,55 +369,37 @@ void FakeAudioInputStream::PlayBeep() { |
| audio_bus_->FromInterleaved( |
| buffer_.get(), audio_bus_->frames(), params_.bits_per_sample() / 8); |
| - callback_->OnData(this, audio_bus_.get(), buffer_size_, 1.0); |
| -} |
| - |
| -void FakeAudioInputStream::Stop() { |
| - DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| - weak_factory_.InvalidateWeakPtrs(); |
| - callback_ = NULL; |
| -} |
| - |
| -void FakeAudioInputStream::Close() { |
| - DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| - audio_manager_->ReleaseInputStream(this); |
| -} |
| - |
| -double FakeAudioInputStream::GetMaxVolume() { |
| - DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| - return 1.0; |
| -} |
| - |
| -void FakeAudioInputStream::SetVolume(double volume) { |
| - DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| -} |
| - |
| -double FakeAudioInputStream::GetVolume() { |
| - DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| - return 1.0; |
| -} |
| - |
| -bool FakeAudioInputStream::IsMuted() { |
| - DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); |
| - return false; |
| + { |
| + base::AutoLock scoped_lock(input_cb_lock_); |
| + if (input_cb_.is_null()) |
| + return; |
| + input_cb_.Run(audio_bus_.get(), buffer_size_); |
| + } |
| } |
| -bool FakeAudioInputStream::SetAutomaticGainControl(bool enabled) { |
| - return false; |
| +void FakeAudioProvider::Worker::Stop() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + { |
| + base::AutoLock scoped_lock(input_cb_lock_); |
| + if (input_cb_.is_null()) |
| + return; |
| + input_cb_.Reset(); |
| + } |
| + worker_task_runner_->PostTask(FROM_HERE, base::Bind(&Worker::DoCancel, this)); |
| } |
| -bool FakeAudioInputStream::GetAutomaticGainControl() { |
| - return false; |
| +bool FakeAudioProvider::Worker::IsStopped() { |
| + base::AutoLock scoped_lock(input_cb_lock_); |
| + return input_cb_.is_null(); |
| } |
| -// static |
| -void FakeAudioInputStream::BeepOnce() { |
| - BeepContext* beep_context = g_beep_context.Pointer(); |
| - beep_context->SetBeepOnce(true); |
| +void FakeAudioProvider::Worker::DoCancel() { |
| + input_task_cb_.Cancel(); |
| } |
| -double FakeAudioInputStream::ProvideInput(AudioBus* audio_bus_into_converter, |
| - base::TimeDelta buffer_delay) { |
| +double FakeAudioProvider::Worker::ProvideInput( |
| + AudioBus* audio_bus_into_converter, |
| + base::TimeDelta buffer_delay) { |
| // Unfilled frames will be zeroed by CopyTo. |
| size_t bytes_written; |
| wav_audio_handler_->CopyTo(audio_bus_into_converter, wav_file_read_pos_, |
| @@ -329,4 +408,9 @@ double FakeAudioInputStream::ProvideInput(AudioBus* audio_bus_into_converter, |
| return 1.0; |
| }; |
| +void FakeAudioProvider::BeepOnce() { |
| + BeepContext* beep_context = g_beep_context.Pointer(); |
| + beep_context->SetBeepOnce(true); |
| +} |
| + |
| } // namespace media |