// 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::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 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::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 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