blob: 0df5bc1ca69d0dcd7197eca93e6259daaa9ae3ac [file] [log] [blame]
// 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<SLuint32>(params.channels());
// Provides sampling rate in milliHertz to OpenSLES.
format_.samplesPerSec = static_cast<SLuint32>(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<SLuint32>(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<SLuint32>(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<OpenSLESInputStream*>(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