| Index: content/renderer/media/audio_input_device.cc
|
| ===================================================================
|
| --- content/renderer/media/audio_input_device.cc (revision 148738)
|
| +++ content/renderer/media/audio_input_device.cc (working copy)
|
| @@ -1,343 +0,0 @@
|
| -// 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 "content/renderer/media/audio_input_device.h"
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/message_loop.h"
|
| -#include "base/threading/thread_restrictions.h"
|
| -#include "base/time.h"
|
| -#include "media/audio/audio_manager_base.h"
|
| -#include "media/audio/audio_util.h"
|
| -
|
| -// Takes care of invoking the capture callback on the audio thread.
|
| -// An instance of this class is created for each capture stream in
|
| -// OnLowLatencyCreated().
|
| -class AudioInputDevice::AudioThreadCallback
|
| - : public AudioDeviceThread::Callback {
|
| - public:
|
| - AudioThreadCallback(const media::AudioParameters& audio_parameters,
|
| - base::SharedMemoryHandle memory,
|
| - int memory_length,
|
| - CaptureCallback* capture_callback);
|
| - virtual ~AudioThreadCallback();
|
| -
|
| - virtual void MapSharedMemory() OVERRIDE;
|
| -
|
| - // Called whenever we receive notifications about pending data.
|
| - virtual void Process(int pending_data) OVERRIDE;
|
| -
|
| - private:
|
| - CaptureCallback* capture_callback_;
|
| - DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback);
|
| -};
|
| -
|
| -AudioInputDevice::AudioInputDevice(
|
| - media::AudioInputIPC* ipc,
|
| - const scoped_refptr<base::MessageLoopProxy>& io_loop)
|
| - : ScopedLoopObserver(io_loop),
|
| - callback_(NULL),
|
| - event_handler_(NULL),
|
| - ipc_(ipc),
|
| - stream_id_(0),
|
| - session_id_(0),
|
| - pending_device_ready_(false),
|
| - agc_is_enabled_(false) {
|
| - CHECK(ipc_);
|
| -}
|
| -
|
| -void AudioInputDevice::Initialize(const media::AudioParameters& params,
|
| - CaptureCallback* callback,
|
| - CaptureEventHandler* event_handler) {
|
| - DCHECK(!callback_);
|
| - DCHECK(!event_handler_);
|
| - audio_parameters_ = params;
|
| - callback_ = callback;
|
| - event_handler_ = event_handler;
|
| -}
|
| -
|
| -void AudioInputDevice::SetDevice(int session_id) {
|
| - DVLOG(1) << "SetDevice (session_id=" << session_id << ")";
|
| - message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id));
|
| -}
|
| -
|
| -void AudioInputDevice::Start() {
|
| - DVLOG(1) << "Start()";
|
| - message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioInputDevice::InitializeOnIOThread, this));
|
| -}
|
| -
|
| -void AudioInputDevice::Stop() {
|
| - DVLOG(1) << "Stop()";
|
| -
|
| - {
|
| - base::AutoLock auto_lock(audio_thread_lock_);
|
| - audio_thread_.Stop(MessageLoop::current());
|
| - }
|
| -
|
| - message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioInputDevice::ShutDownOnIOThread, this));
|
| -}
|
| -
|
| -void AudioInputDevice::SetVolume(double volume) {
|
| - if (volume < 0 || volume > 1.0) {
|
| - DLOG(ERROR) << "Invalid volume value specified";
|
| - return;
|
| - }
|
| -
|
| - message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioInputDevice::SetVolumeOnIOThread, this, volume));
|
| -}
|
| -
|
| -void AudioInputDevice::SetAutomaticGainControl(bool enabled) {
|
| - DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")";
|
| - message_loop()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread,
|
| - this, enabled));
|
| -}
|
| -
|
| -void AudioInputDevice::OnStreamCreated(
|
| - base::SharedMemoryHandle handle,
|
| - base::SyncSocket::Handle socket_handle,
|
| - int length) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| -#if defined(OS_WIN)
|
| - DCHECK(handle);
|
| - DCHECK(socket_handle);
|
| -#else
|
| - DCHECK_GE(handle.fd, 0);
|
| - DCHECK_GE(socket_handle, 0);
|
| -#endif
|
| - DCHECK(length);
|
| - DVLOG(1) << "OnStreamCreated (stream_id=" << stream_id_ << ")";
|
| -
|
| - // We should only get this callback if stream_id_ is valid. If it is not,
|
| - // the IPC layer should have closed the shared memory and socket handles
|
| - // for us and not invoked the callback. The basic assertion is that when
|
| - // stream_id_ is 0 the AudioInputDevice instance is not registered as a
|
| - // delegate and hence it should not receive callbacks.
|
| - DCHECK(stream_id_);
|
| -
|
| - base::AutoLock auto_lock(audio_thread_lock_);
|
| -
|
| - DCHECK(audio_thread_.IsStopped());
|
| - audio_callback_.reset(
|
| - new AudioInputDevice::AudioThreadCallback(audio_parameters_, handle,
|
| - length, callback_));
|
| - audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice");
|
| -
|
| - MessageLoop::current()->PostTask(FROM_HERE,
|
| - base::Bind(&AudioInputDevice::StartOnIOThread, this));
|
| -}
|
| -
|
| -void AudioInputDevice::OnVolume(double volume) {
|
| - NOTIMPLEMENTED();
|
| -}
|
| -
|
| -void AudioInputDevice::OnStateChanged(
|
| - media::AudioInputIPCDelegate::State state) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| -
|
| - // Do nothing if the stream has been closed.
|
| - if (!stream_id_)
|
| - return;
|
| -
|
| - switch (state) {
|
| - case media::AudioInputIPCDelegate::kStopped:
|
| - // TODO(xians): Should we just call ShutDownOnIOThread here instead?
|
| - ipc_->RemoveDelegate(stream_id_);
|
| -
|
| - audio_thread_.Stop(MessageLoop::current());
|
| - audio_callback_.reset();
|
| -
|
| - if (event_handler_)
|
| - event_handler_->OnDeviceStopped();
|
| -
|
| - stream_id_ = 0;
|
| - pending_device_ready_ = false;
|
| - break;
|
| - case media::AudioInputIPCDelegate::kRecording:
|
| - NOTIMPLEMENTED();
|
| - break;
|
| - case media::AudioInputIPCDelegate::kError:
|
| - DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)";
|
| - // Don't dereference the callback object if the audio thread
|
| - // is stopped or stopping. That could mean that the callback
|
| - // object has been deleted.
|
| - // TODO(tommi): Add an explicit contract for clearing the callback
|
| - // object. Possibly require calling Initialize again or provide
|
| - // a callback object via Start() and clear it in Stop().
|
| - if (!audio_thread_.IsStopped())
|
| - callback_->OnCaptureError();
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| -}
|
| -
|
| -void AudioInputDevice::OnDeviceReady(const std::string& device_id) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")";
|
| -
|
| - // Takes care of the case when Stop() is called before OnDeviceReady().
|
| - if (!pending_device_ready_)
|
| - return;
|
| -
|
| - // If AudioInputDeviceManager returns an empty string, it means no device
|
| - // is ready for start.
|
| - if (device_id.empty()) {
|
| - ipc_->RemoveDelegate(stream_id_);
|
| - stream_id_ = 0;
|
| - } else {
|
| - ipc_->CreateStream(stream_id_, audio_parameters_, device_id,
|
| - agc_is_enabled_);
|
| - }
|
| -
|
| - pending_device_ready_ = false;
|
| - // Notify the client that the device has been started.
|
| - if (event_handler_)
|
| - event_handler_->OnDeviceStarted(device_id);
|
| -}
|
| -
|
| -void AudioInputDevice::OnIPCClosed() {
|
| - ipc_ = NULL;
|
| -}
|
| -
|
| -AudioInputDevice::~AudioInputDevice() {
|
| - // TODO(henrika): The current design requires that the user calls
|
| - // Stop before deleting this class.
|
| - CHECK_EQ(0, stream_id_);
|
| -}
|
| -
|
| -void AudioInputDevice::InitializeOnIOThread() {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - // Make sure we don't call Start() more than once.
|
| - DCHECK_EQ(0, stream_id_);
|
| - if (stream_id_)
|
| - return;
|
| -
|
| - stream_id_ = ipc_->AddDelegate(this);
|
| - // If |session_id_| is not specified, it will directly create the stream;
|
| - // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser
|
| - // and create the stream when getting a OnDeviceReady() callback.
|
| - if (!session_id_) {
|
| - ipc_->CreateStream(stream_id_, audio_parameters_,
|
| - media::AudioManagerBase::kDefaultDeviceId, agc_is_enabled_);
|
| - } else {
|
| - ipc_->StartDevice(stream_id_, session_id_);
|
| - pending_device_ready_ = true;
|
| - }
|
| -}
|
| -
|
| -void AudioInputDevice::SetSessionIdOnIOThread(int session_id) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - session_id_ = session_id;
|
| -}
|
| -
|
| -void AudioInputDevice::StartOnIOThread() {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - if (stream_id_)
|
| - ipc_->RecordStream(stream_id_);
|
| -}
|
| -
|
| -void AudioInputDevice::ShutDownOnIOThread() {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - // NOTE: |completion| may be NULL.
|
| - // Make sure we don't call shutdown more than once.
|
| - if (stream_id_) {
|
| - ipc_->CloseStream(stream_id_);
|
| - ipc_->RemoveDelegate(stream_id_);
|
| -
|
| - stream_id_ = 0;
|
| - session_id_ = 0;
|
| - pending_device_ready_ = false;
|
| - agc_is_enabled_ = false;
|
| - }
|
| -
|
| - // We can run into an issue where ShutDownOnIOThread is called right after
|
| - // OnStreamCreated is called in cases where Start/Stop are called before we
|
| - // get the OnStreamCreated callback. To handle that corner case, we call
|
| - // Stop(). In most cases, the thread will already be stopped.
|
| - // Another situation is when the IO thread goes away before Stop() is called
|
| - // in which case, we cannot use the message loop to close the thread handle
|
| - // and can't not rely on the main thread existing either.
|
| - base::ThreadRestrictions::ScopedAllowIO allow_io;
|
| - audio_thread_.Stop(NULL);
|
| - audio_callback_.reset();
|
| -}
|
| -
|
| -void AudioInputDevice::SetVolumeOnIOThread(double volume) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - if (stream_id_)
|
| - ipc_->SetVolume(stream_id_, volume);
|
| -}
|
| -
|
| -void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) {
|
| - DCHECK(message_loop()->BelongsToCurrentThread());
|
| - DCHECK_EQ(0, stream_id_) <<
|
| - "The AGC state can not be modified while capturing is active.";
|
| - if (stream_id_)
|
| - return;
|
| -
|
| - // We simply store the new AGC setting here. This value will be used when
|
| - // a new stream is initialized and by GetAutomaticGainControl().
|
| - agc_is_enabled_ = enabled;
|
| -}
|
| -
|
| -void AudioInputDevice::WillDestroyCurrentMessageLoop() {
|
| - LOG(ERROR) << "IO loop going away before the input device has been stopped";
|
| - ShutDownOnIOThread();
|
| -}
|
| -
|
| -// AudioInputDevice::AudioThreadCallback
|
| -AudioInputDevice::AudioThreadCallback::AudioThreadCallback(
|
| - const media::AudioParameters& audio_parameters,
|
| - base::SharedMemoryHandle memory,
|
| - int memory_length,
|
| - CaptureCallback* capture_callback)
|
| - : AudioDeviceThread::Callback(audio_parameters, memory, memory_length),
|
| - capture_callback_(capture_callback) {
|
| -}
|
| -
|
| -AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() {
|
| -}
|
| -
|
| -void AudioInputDevice::AudioThreadCallback::MapSharedMemory() {
|
| - shared_memory_.Map(memory_length_);
|
| -}
|
| -
|
| -void AudioInputDevice::AudioThreadCallback::Process(int pending_data) {
|
| - // The shared memory represents parameters, size of the data buffer and the
|
| - // actual data buffer containing audio data. Map the memory into this
|
| - // structure and parse out parameters and the data area.
|
| - media::AudioInputBuffer* buffer =
|
| - reinterpret_cast<media::AudioInputBuffer*>(shared_memory_.memory());
|
| - DCHECK_EQ(buffer->params.size,
|
| - memory_length_ - sizeof(media::AudioInputBufferParameters));
|
| - double volume = buffer->params.volume;
|
| -
|
| - int audio_delay_milliseconds = pending_data / bytes_per_ms_;
|
| - int16* memory = reinterpret_cast<int16*>(&buffer->audio[0]);
|
| - const size_t number_of_frames = audio_parameters_.frames_per_buffer();
|
| - const int bytes_per_sample = sizeof(memory[0]);
|
| -
|
| - // Deinterleave each channel and convert to 32-bit floating-point
|
| - // with nominal range -1.0 -> +1.0.
|
| - for (int channel_index = 0; channel_index < audio_parameters_.channels();
|
| - ++channel_index) {
|
| - media::DeinterleaveAudioChannel(memory,
|
| - audio_data_[channel_index],
|
| - audio_parameters_.channels(),
|
| - channel_index,
|
| - bytes_per_sample,
|
| - number_of_frames);
|
| - }
|
| -
|
| - // Deliver captured data to the client in floating point format
|
| - // and update the audio-delay measurement.
|
| - capture_callback_->Capture(audio_data_, number_of_frames,
|
| - audio_delay_milliseconds, volume);
|
| -}
|
|
|