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 |