blob: 1e264e6039f06d30845dfd400afa637f85f3b62e [file] [log] [blame]
// Copyright (c) 2011 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_controller.h"
#include "base/bind.h"
#include "base/threading/thread_restrictions.h"
#include "media/base/limits.h"
namespace {
const int kMaxInputChannels = 2;
const int kTimerResetInterval = 1; // One second.
}
namespace media {
// static
AudioInputController::Factory* AudioInputController::factory_ = NULL;
AudioInputController::AudioInputController(EventHandler* handler,
SyncWriter* sync_writer)
: handler_(handler),
stream_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(no_data_timer_(FROM_HERE,
base::TimeDelta::FromSeconds(kTimerResetInterval),
this,
&AudioInputController::DoReportNoDataError)),
state_(kEmpty),
thread_("AudioInputControllerThread"),
sync_writer_(sync_writer) {
}
AudioInputController::~AudioInputController() {
DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_);
}
// static
scoped_refptr<AudioInputController> AudioInputController::Create(
EventHandler* event_handler,
const AudioParameters& params) {
if (!params.IsValid() || (params.channels > kMaxInputChannels))
return NULL;
if (factory_) {
return factory_->Create(event_handler, params);
}
scoped_refptr<AudioInputController> controller(new AudioInputController(
event_handler, NULL));
// Start the thread and post a task to create the audio input stream.
controller->thread_.Start();
controller->thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
&AudioInputController::DoCreate, controller.get(), params));
return controller;
}
// static
scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency(
EventHandler* event_handler,
const AudioParameters& params,
SyncWriter* sync_writer) {
DCHECK(sync_writer);
if (!params.IsValid() || (params.channels > kMaxInputChannels))
return NULL;
if (!AudioManager::GetAudioManager())
return NULL;
// Starts the audio controller thread.
scoped_refptr<AudioInputController> controller(new AudioInputController(
event_handler, sync_writer));
// Start the thread and post a task to create the audio input stream.
controller->thread_.Start();
controller->thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
&AudioInputController::DoCreate, controller.get(), params));
return controller;
}
void AudioInputController::Record() {
DCHECK(thread_.IsRunning());
thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
&AudioInputController::DoRecord, this));
}
void AudioInputController::Close() {
if (!thread_.IsRunning()) {
// If the thread is not running make sure we are stopped.
DCHECK_EQ(kClosed, state_);
return;
}
// Wait for all tasks to complete on the audio thread.
thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
&AudioInputController::DoClose, this));
// A ScopedAllowIO object is required to join the thread when calling Stop.
// This is because as joining threads may be a long operation it's now
// not allowed in threads without IO access, which is the case of the IO
// thread (it is missnamed) being used here. This object overrides
// temporarily this restriction and should be used only in specific
// infrequent cases where joining is guaranteed to be fast.
// Bug: http://code.google.com/p/chromium/issues/detail?id=67806
base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join;
thread_.Stop();
}
void AudioInputController::DoCreate(const AudioParameters& params) {
stream_ = AudioManager::GetAudioManager()->MakeAudioInputStream(params);
if (!stream_) {
// TODO(satish): Define error types.
handler_->OnError(this, 0);
return;
}
if (stream_ && !stream_->Open()) {
stream_->Close();
stream_ = NULL;
// TODO(satish): Define error types.
handler_->OnError(this, 0);
return;
}
thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
&AudioInputController::DoResetNoDataTimer, this));
state_ = kCreated;
handler_->OnCreated(this);
}
void AudioInputController::DoRecord() {
DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
if (state_ != kCreated)
return;
{
base::AutoLock auto_lock(lock_);
state_ = kRecording;
}
stream_->Start(this);
handler_->OnRecording(this);
}
void AudioInputController::DoClose() {
DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
DCHECK_NE(kClosed, state_);
// |stream_| can be null if creating the device failed in DoCreate().
if (stream_) {
stream_->Stop();
stream_->Close();
// After stream is closed it is destroyed, so don't keep a reference to it.
stream_ = NULL;
}
if (LowLatencyMode()) {
sync_writer_->Close();
}
// Since the stream is closed at this point there's no other threads reading
// |state_| so we don't need to lock.
state_ = kClosed;
}
void AudioInputController::DoReportError(int code) {
DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
handler_->OnError(this, code);
}
void AudioInputController::DoReportNoDataError() {
DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
handler_->OnError(this, 0);
}
void AudioInputController::DoResetNoDataTimer() {
DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
no_data_timer_.Reset();
}
void AudioInputController::OnData(AudioInputStream* stream, const uint8* data,
uint32 size, uint32 hardware_delay_bytes) {
{
base::AutoLock auto_lock(lock_);
if (state_ != kRecording)
return;
}
thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
&AudioInputController::DoResetNoDataTimer, this));
// Use SyncSocket if we are in a low-latency mode.
if (LowLatencyMode()) {
sync_writer_->Write(data, size);
sync_writer_->UpdateRecordedBytes(hardware_delay_bytes);
return;
}
handler_->OnData(this, data, size);
}
void AudioInputController::OnClose(AudioInputStream* stream) {
// TODO(satish): Sometimes the device driver closes the input stream without
// us asking for it (may be if the device was unplugged?). Check how to handle
// such cases here.
}
void AudioInputController::OnError(AudioInputStream* stream, int code) {
// Handle error on the audio controller thread.
thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
&AudioInputController::DoReportError, this, code));
}
} // namespace media