// Copyright (c) 2012 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/virtual_audio_input_stream.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "media/audio/virtual_audio_output_stream.h"
namespace media {
// LoopbackAudioConverter works similar to AudioConverter and converts input
// streams to different audio parameters. Then, the LoopbackAudioConverter can
// be used as an input to another AudioConverter. This allows us to
// use converted audio from AudioOutputStreams as input to an AudioConverter.
// For example, this allows converting multiple streams into a common format and
// using the converted audio as input to another AudioConverter (i.e. a mixer).
class LoopbackAudioConverter : public AudioConverter::InputCallback {
public:
LoopbackAudioConverter(const AudioParameters& input_params,
const AudioParameters& output_params)
: audio_converter_(input_params, output_params, false) {}
virtual ~LoopbackAudioConverter() {}
void AddInput(AudioConverter::InputCallback* input) {
audio_converter_.AddInput(input);
}
void RemoveInput(AudioConverter::InputCallback* input) {
audio_converter_.RemoveInput(input);
}
private:
virtual double ProvideInput(AudioBus* audio_bus,
base::TimeDelta buffer_delay) OVERRIDE {
audio_converter_.Convert(audio_bus);
return 1.0;
}
AudioConverter audio_converter_;
DISALLOW_COPY_AND_ASSIGN(LoopbackAudioConverter);
};
VirtualAudioInputStream::VirtualAudioInputStream(
const AudioParameters& params,
const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner,
const AfterCloseCallback& after_close_cb)
: worker_task_runner_(worker_task_runner),
after_close_cb_(after_close_cb),
callback_(NULL),
buffer_(new uint8[params.GetBytesPerBuffer()]),
params_(params),
mixer_(params_, params_, false),
num_attached_output_streams_(0),
fake_consumer_(worker_task_runner_, params_) {
DCHECK(params_.IsValid());
DCHECK(worker_task_runner_.get());
// VAIS can be constructed on any thread, but will DCHECK that all
// AudioInputStream methods are called from the same thread.
thread_checker_.DetachFromThread();
}
VirtualAudioInputStream::~VirtualAudioInputStream() {
DCHECK(!callback_);
// Sanity-check: Contract for Add/RemoveOutputStream() requires that all
// output streams be removed before VirtualAudioInputStream is destroyed.
DCHECK_EQ(0, num_attached_output_streams_);
for (AudioConvertersMap::iterator it = converters_.begin();
it != converters_.end(); ++it) {
delete it->second;
}
}
bool VirtualAudioInputStream::Open() {
DCHECK(thread_checker_.CalledOnValidThread());
memset(buffer_.get(), 0, params_.GetBytesPerBuffer());
return true;
}
void VirtualAudioInputStream::Start(AudioInputCallback* callback) {
DCHECK(thread_checker_.CalledOnValidThread());
callback_ = callback;
fake_consumer_.Start(base::Bind(
&VirtualAudioInputStream::PumpAudio, base::Unretained(this)));
}
void VirtualAudioInputStream::Stop() {
DCHECK(thread_checker_.CalledOnValidThread());
fake_consumer_.Stop();
callback_ = NULL;
}
void VirtualAudioInputStream::AddOutputStream(
VirtualAudioOutputStream* stream, const AudioParameters& output_params) {
DCHECK(thread_checker_.CalledOnValidThread());
base::AutoLock scoped_lock(converter_network_lock_);
AudioConvertersMap::iterator converter = converters_.find(output_params);
if (converter == converters_.end()) {
std::pair<AudioConvertersMap::iterator, bool> result = converters_.insert(
std::make_pair(output_params,
new LoopbackAudioConverter(output_params, params_)));
converter = result.first;
// Add to main mixer if we just added a new AudioTransform.
mixer_.AddInput(converter->second);
}
converter->second->AddInput(stream);
++num_attached_output_streams_;
}
void VirtualAudioInputStream::RemoveOutputStream(
VirtualAudioOutputStream* stream, const AudioParameters& output_params) {
DCHECK(thread_checker_.CalledOnValidThread());
base::AutoLock scoped_lock(converter_network_lock_);
DCHECK(converters_.find(output_params) != converters_.end());
converters_[output_params]->RemoveInput(stream);
--num_attached_output_streams_;
DCHECK_LE(0, num_attached_output_streams_);
}
void VirtualAudioInputStream::PumpAudio(AudioBus* audio_bus) {
DCHECK(worker_task_runner_->BelongsToCurrentThread());
{
base::AutoLock scoped_lock(converter_network_lock_);
mixer_.Convert(audio_bus);
}
callback_->OnData(this, audio_bus, params_.GetBytesPerBuffer(), 1.0);
}
void VirtualAudioInputStream::Close() {
DCHECK(thread_checker_.CalledOnValidThread());
Stop(); // Make sure callback_ is no longer being used.
// If a non-null AfterCloseCallback was provided to the constructor, invoke it
// here. The callback is moved to a stack-local first since |this| could be
// destroyed during Run().
if (!after_close_cb_.is_null()) {
const AfterCloseCallback cb = after_close_cb_;
after_close_cb_.Reset();
cb.Run(this);
}
}
double VirtualAudioInputStream::GetMaxVolume() {
return 1.0;
}
void VirtualAudioInputStream::SetVolume(double volume) {}
double VirtualAudioInputStream::GetVolume() {
return 1.0;
}
void VirtualAudioInputStream::SetAutomaticGainControl(bool enabled) {}
bool VirtualAudioInputStream::GetAutomaticGainControl() {
return false;
}
} // namespace media