// 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/android/opensles_input.h" #include "base/logging.h" #include "media/audio/android/audio_manager_android.h" namespace media { OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager, const AudioParameters& params) : audio_manager_(audio_manager), callback_(NULL), recorder_(NULL), simple_buffer_queue_(NULL), active_queue_(0), buffer_size_bytes_(0), started_(false) { format_.formatType = SL_DATAFORMAT_PCM; format_.numChannels = static_cast(params.channels()); // Provides sampling rate in milliHertz to OpenSLES. format_.samplesPerSec = static_cast(params.sample_rate() * 1000); format_.bitsPerSample = params.bits_per_sample(); format_.containerSize = params.bits_per_sample(); format_.endianness = SL_BYTEORDER_LITTLEENDIAN; if (format_.numChannels == 1) format_.channelMask = SL_SPEAKER_FRONT_CENTER; else if (format_.numChannels == 2) format_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; else NOTREACHED() << "Unsupported number of channels: " << format_.numChannels; buffer_size_bytes_ = params.GetBytesPerBuffer(); memset(&audio_data_, 0, sizeof(audio_data_)); } OpenSLESInputStream::~OpenSLESInputStream() { DCHECK(!recorder_object_.Get()); DCHECK(!engine_object_.Get()); DCHECK(!recorder_); DCHECK(!simple_buffer_queue_); DCHECK(!audio_data_[0]); } bool OpenSLESInputStream::Open() { if (engine_object_.Get()) return false; if (!CreateRecorder()) return false; SetupAudioBuffer(); return true; } void OpenSLESInputStream::Start(AudioInputCallback* callback) { DCHECK(callback); DCHECK(recorder_); DCHECK(simple_buffer_queue_); if (started_) return; // Enable the flags before streaming. callback_ = callback; active_queue_ = 0; started_ = true; SLresult err = SL_RESULT_UNKNOWN_ERROR; // Enqueues |kNumOfQueuesInBuffer| zero buffers to get the ball rolling. for (int i = 0; i < kNumOfQueuesInBuffer - 1; ++i) { err = (*simple_buffer_queue_)->Enqueue( simple_buffer_queue_, audio_data_[i], buffer_size_bytes_); if (SL_RESULT_SUCCESS != err) { HandleError(err); return; } } // Start the recording by setting the state to |SL_RECORDSTATE_RECORDING|. err = (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_RECORDING); DCHECK_EQ(SL_RESULT_SUCCESS, err); if (SL_RESULT_SUCCESS != err) HandleError(err); } void OpenSLESInputStream::Stop() { if (!started_) return; // Stop recording by setting the record state to |SL_RECORDSTATE_STOPPED|. SLresult err = (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_STOPPED); if (SL_RESULT_SUCCESS != err) { DLOG(WARNING) << "SetRecordState() failed to set the state to stop"; } // Clear the buffer queue to get rid of old data when resuming recording. err = (*simple_buffer_queue_)->Clear(simple_buffer_queue_); if (SL_RESULT_SUCCESS != err) { DLOG(WARNING) << "Clear() failed to clear the buffer queue"; } started_ = false; } void OpenSLESInputStream::Close() { // Stop the stream if it is still recording. Stop(); // Explicitly free the player objects and invalidate their associated // interfaces. They have to be done in the correct order. recorder_object_.Reset(); engine_object_.Reset(); simple_buffer_queue_ = NULL; recorder_ = NULL; ReleaseAudioBuffer(); audio_manager_->ReleaseInputStream(this); } double OpenSLESInputStream::GetMaxVolume() { NOTIMPLEMENTED(); return 0.0; } void OpenSLESInputStream::SetVolume(double volume) { NOTIMPLEMENTED(); } double OpenSLESInputStream::GetVolume() { NOTIMPLEMENTED(); return 0.0; } void OpenSLESInputStream::SetAutomaticGainControl(bool enabled) { NOTIMPLEMENTED(); } bool OpenSLESInputStream::GetAutomaticGainControl() { NOTIMPLEMENTED(); return false; } bool OpenSLESInputStream::CreateRecorder() { // Initializes the engine object with specific option. After working with the // object, we need to free the object and its resources. SLEngineOption option[] = { { SL_ENGINEOPTION_THREADSAFE, static_cast(SL_BOOLEAN_TRUE) } }; SLresult err = slCreateEngine(engine_object_.Receive(), 1, option, 0, NULL, NULL); DCHECK_EQ(SL_RESULT_SUCCESS, err); if (SL_RESULT_SUCCESS != err) return false; // Realize the SL engine object in synchronous mode. err = engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE); DCHECK_EQ(SL_RESULT_SUCCESS, err); if (SL_RESULT_SUCCESS != err) return false; // Get the SL engine interface which is implicit. SLEngineItf engine; err = engine_object_->GetInterface( engine_object_.Get(), SL_IID_ENGINE, &engine); DCHECK_EQ(SL_RESULT_SUCCESS, err); if (SL_RESULT_SUCCESS != err) return false; // Audio source configuration. SLDataLocator_IODevice mic_locator = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; SLDataSource audio_source = { &mic_locator, NULL }; // Audio sink configuration. SLDataLocator_AndroidSimpleBufferQueue buffer_queue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // Locator type. static_cast(kNumOfQueuesInBuffer) // Number of buffers. }; SLDataSink audio_sink = { &buffer_queue, &format_ }; // Create an audio recorder. const SLuint32 number_of_interfaces = 1; const SLInterfaceID interface_id[number_of_interfaces] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; const SLboolean interface_required[number_of_interfaces] = { SL_BOOLEAN_TRUE }; err = (*engine)->CreateAudioRecorder(engine, recorder_object_.Receive(), &audio_source, &audio_sink, number_of_interfaces, interface_id, interface_required); DCHECK_EQ(SL_RESULT_SUCCESS, err); if (SL_RESULT_SUCCESS != err) { DLOG(ERROR) << "CreateAudioRecorder failed with error code " << err; return false; } // Realize the recorder object in synchronous mode. err = recorder_object_->Realize(recorder_object_.Get(), SL_BOOLEAN_FALSE); DCHECK_EQ(SL_RESULT_SUCCESS, err); if (SL_RESULT_SUCCESS != err) { DLOG(ERROR) << "Recprder Realize() failed with error code " << err; return false; } // Get an implicit recorder interface. err = recorder_object_->GetInterface(recorder_object_.Get(), SL_IID_RECORD, &recorder_); DCHECK_EQ(SL_RESULT_SUCCESS, err); if (SL_RESULT_SUCCESS != err) return false; // Get the simple buffer queue interface. err = recorder_object_->GetInterface(recorder_object_.Get(), SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &simple_buffer_queue_); DCHECK_EQ(SL_RESULT_SUCCESS, err); if (SL_RESULT_SUCCESS != err) return false; // Register the input callback for the simple buffer queue. // This callback will be called when receiving new data from the device. err = (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_, SimpleBufferQueueCallback, this); DCHECK_EQ(SL_RESULT_SUCCESS, err); return (SL_RESULT_SUCCESS == err); } void OpenSLESInputStream::SimpleBufferQueueCallback( SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) { OpenSLESInputStream* stream = reinterpret_cast(instance); stream->ReadBufferQueue(); } void OpenSLESInputStream::ReadBufferQueue() { if (!started_) return; // Get the enqueued buffer from the soundcard. SLresult err = (*simple_buffer_queue_)->Enqueue( simple_buffer_queue_, audio_data_[active_queue_], buffer_size_bytes_); if (SL_RESULT_SUCCESS != err) HandleError(err); // TODO(xians): Get an accurate delay estimation. callback_->OnData(this, audio_data_[active_queue_], buffer_size_bytes_, buffer_size_bytes_, 0.0); active_queue_ = (active_queue_ + 1) % kNumOfQueuesInBuffer; } void OpenSLESInputStream::SetupAudioBuffer() { DCHECK(!audio_data_[0]); for (int i = 0; i < kNumOfQueuesInBuffer; ++i) { audio_data_[i] = new uint8[buffer_size_bytes_]; } } void OpenSLESInputStream::ReleaseAudioBuffer() { if (audio_data_[0]) { for (int i = 0; i < kNumOfQueuesInBuffer; ++i) { delete [] audio_data_[i]; audio_data_[i] = NULL; } } } void OpenSLESInputStream::HandleError(SLresult error) { DLOG(FATAL) << "OpenSLES error " << error; if (callback_) callback_->OnError(this, error); } } // namespace media