| 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);
|
| +
|
| + // 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
|
|
|