Index: media/audio/audio_input_proxy.cc |
diff --git a/media/audio/audio_input_proxy.cc b/media/audio/audio_input_proxy.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..988a412db30f62c3415a0fbe39d7a0c1764b92c6 |
--- /dev/null |
+++ b/media/audio/audio_input_proxy.cc |
@@ -0,0 +1,182 @@ |
+// Copyright 2014 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/audio_input_proxy.h" |
+ |
+#include "base/logging.h" |
+#include "base/time/time.h" |
+#include "media/audio/audio_manager_base.h" |
+#include "media/base/audio_fifo.h" |
+ |
+namespace media { |
+ |
+AudioInputProxy::AudioInputProxy(AudioManagerBase* manager, |
+ const AudioParameters& input_params, |
+ const AudioParameters& output_params, |
+ const std::string& device_id) |
+ : audio_manager_(manager), |
+ stream_(NULL), |
+ input_params_(input_params), |
+ output_params_(output_params), |
+ device_id_(device_id), |
+ callback_(NULL), |
+ opened_(false), |
+ agc_enabled_(false) { |
+ DCHECK(input_params.IsValid()); |
+ DCHECK(output_params.IsValid()); |
+ DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); |
+ |
+ Initialize(input_params); |
+} |
+ |
+AudioInputProxy::~AudioInputProxy() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ Close(); |
+} |
+ |
+void AudioInputProxy::MaybeRestartStream(const AudioParameters& input_params) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ // Do nothing if the required input formats are the same. |
+ if (input_params == input_params_) |
+ return; |
+ |
+ // Do nothing if |stream_| has already been closed, which means this proxy |
+ // is idle. |
+ if (!stream_) |
+ return; |
+ |
+ // Cache the |callback_| because Stop() will set it to NULL> |
+ AudioInputStream::AudioInputCallback* callback = callback_; |
+ |
+ // Stop and Close the old stream. |
+ Stop(); |
+ Close(); |
+ DCHECK(!stream_); |
+ |
+ // Initialize the new stream. |
+ Initialize(input_params); |
+ |
+ // Create and restore the previous state for the new stream. |
+ if (opened_) |
+ Open(); |
+ |
+ // AGC needs to be enabled before calling Start(); |
+ SetAutomaticGainControl(agc_enabled_); |
+ |
+ if (callback) |
+ Start(callback); |
+ DCHECK_EQ(callback, callback_); |
+} |
+ |
+void AudioInputProxy::Initialize(const AudioParameters& input_params) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ // We do not support resampling and down/up-mixing yet. |
+ // TODO(xians): Implement a push model AudioConverter and support resampling. |
+ DCHECK_EQ(output_params_.sample_rate(), input_params.sample_rate()); |
+ DCHECK_EQ(output_params_.channels(), input_params.channels()); |
+ DCHECK(!stream_); |
+ |
+ input_params_ = input_params; |
+ |
+ // Create the required audio fifo and audio bus to deal with the |
+ // mismatching between the input and output params. |
+ // The size of the FIFO is input buffer size + output buffer size. |
+ // When the input buffer size and the output buffer size are the same, FIFO |
+ // is not needed. |
+ if (input_params_.frames_per_buffer() != output_params_.frames_per_buffer()) { |
+ int buffer_size = |
+ input_params_.frames_per_buffer() + output_params_.frames_per_buffer(); |
+ fifo_.reset(new media::AudioFifo(input_params_.channels(), buffer_size)); |
+ audio_bus_ = media::AudioBus::Create( |
+ output_params_.channels(), output_params_.frames_per_buffer()); |
+ } |
+ |
+ // Create a new audio input stream. |
+ stream_ = audio_manager_->MakeLowLatencyInputStream(input_params, device_id_); |
+} |
+ |
+bool AudioInputProxy::Open() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(!stream_); |
+ |
+ opened_ = true; |
+ return stream_->Open(); |
+} |
+ |
+void AudioInputProxy::Start(AudioInputStream::AudioInputCallback* callback) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(!callback_ || callback_ == callback); |
+ |
+ stream_->Start(callback); |
+ callback_ = callback; |
+} |
+ |
+void AudioInputProxy::Stop() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ stream_->Stop(); |
+ callback_ = NULL; |
+} |
+ |
+void AudioInputProxy::Close() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (!stream_) |
+ return; |
+ |
+ // AudioManager owns the |stream_|. |
+ stream_->Close(); |
+ stream_ = NULL; |
+} |
+ |
+double AudioInputProxy::GetMaxVolume() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ return stream_->GetMaxVolume(); |
+} |
+ |
+void AudioInputProxy::SetVolume(double volume) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ stream_->SetVolume(volume); |
+} |
+ |
+double AudioInputProxy::GetVolume() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ return stream_->GetVolume(); |
+} |
+ |
+void AudioInputProxy::SetAutomaticGainControl(bool enabled) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ stream_->SetAutomaticGainControl(enabled); |
+ agc_enabled_ = enabled; |
+} |
+ |
+bool AudioInputProxy::GetAutomaticGainControl() { |
+ return stream_->GetAutomaticGainControl(); |
+} |
+ |
+void AudioInputProxy::OnData(AudioInputStream* stream, |
+ const media::AudioBus* src, |
+ uint32 hardware_delay_bytes, |
+ double volume) { |
+ // TODO(xians) We need to handle the frames changing on the fly ?? |
+ DCHECK_EQ(src->frames(), input_params_.frames_per_buffer()); |
+ if (!fifo_.get()) |
+ callback_->OnData(stream, src, hardware_delay_bytes, volume); |
+ |
+ DCHECK_LE(fifo_->frames() + src->frames(), fifo_->max_frames()); |
+ fifo_->Push(src); |
+ |
+ // Consume the data if the FIFO has enough data. |
+ while (fifo_->frames() >= output_params_.frames_per_buffer()) { |
+ fifo_->Consume(audio_bus_.get(), 0, audio_bus_->frames()); |
+ callback_->OnData(stream, audio_bus_.get(), hardware_delay_bytes, volume); |
+ } |
+} |
+ |
+void AudioInputProxy::OnError(AudioInputStream* stream) { |
+ callback_->OnError(stream); |
+} |
+ |
+ |
+ |
+} // namespace media |