Chromium Code Reviews| Index: media/audio/android/opensles_input.cc |
| diff --git a/media/audio/android/opensles_input.cc b/media/audio/android/opensles_input.cc |
| index 15c3eac3726389bb7f8d0a7f37ee4b8d35a82f91..ab54fa07c8c2ae0143921b0361a59f4babf6a7df 100644 |
| --- a/media/audio/android/opensles_input.cc |
| +++ b/media/audio/android/opensles_input.cc |
| @@ -4,16 +4,17 @@ |
| #include "media/audio/android/opensles_input.h" |
| +#include "base/debug/trace_event.h" |
| #include "base/logging.h" |
| #include "media/audio/android/audio_manager_android.h" |
| -#define LOG_ON_FAILURE_AND_RETURN(op, ...) \ |
| - do { \ |
| - SLresult err = (op); \ |
| - if (err != SL_RESULT_SUCCESS) { \ |
| +#define LOG_ON_FAILURE_AND_RETURN(op, ...) \ |
| + do { \ |
| + SLresult err = (op); \ |
| + if (err != SL_RESULT_SUCCESS) { \ |
| DLOG(ERROR) << #op << " failed: " << err; \ |
| - return __VA_ARGS__; \ |
| - } \ |
| + return __VA_ARGS__; \ |
| + } \ |
| } while (0) |
| namespace media { |
| @@ -24,9 +25,10 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager, |
| callback_(NULL), |
| recorder_(NULL), |
| simple_buffer_queue_(NULL), |
| - active_queue_(0), |
| + active_buffer_index_(0), |
| buffer_size_bytes_(0), |
| started_(false) { |
| + DVLOG(2) << "OpenSLESInputStream::OpenSLESInputStream()"; |
| format_.formatType = SL_DATAFORMAT_PCM; |
| format_.numChannels = static_cast<SLuint32>(params.channels()); |
| // Provides sampling rate in milliHertz to OpenSLES. |
| @@ -47,6 +49,8 @@ OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager, |
| } |
| OpenSLESInputStream::~OpenSLESInputStream() { |
| + DVLOG(2) << "OpenSLESInputStream::~OpenSLESInputStream()"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!recorder_object_.Get()); |
| DCHECK(!engine_object_.Get()); |
| DCHECK(!recorder_); |
| @@ -55,6 +59,8 @@ OpenSLESInputStream::~OpenSLESInputStream() { |
| } |
| bool OpenSLESInputStream::Open() { |
| + DVLOG(2) << "OpenSLESInputStream::Open()"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| if (engine_object_.Get()) |
| return false; |
| @@ -67,44 +73,59 @@ bool OpenSLESInputStream::Open() { |
| } |
| void OpenSLESInputStream::Start(AudioInputCallback* callback) { |
| + DVLOG(2) << "OpenSLESInputStream::Start()"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(callback); |
| DCHECK(recorder_); |
| DCHECK(simple_buffer_queue_); |
| if (started_) |
| return; |
| - // Enable the flags before streaming. |
| + base::AutoLock lock(lock_); |
| + DCHECK(callback_ == NULL || callback_ == callback); |
| callback_ = callback; |
| - active_queue_ = 0; |
| - started_ = true; |
| + active_buffer_index_ = 0; |
| + // Enqueues kMaxNumOfBuffersInQueue zero buffers to get the ball rolling. |
| + // TODO(henrika): add support for Start/Stop/Start sequences when we are |
| + // able to clear the buffer queue. There is currently a bug in the OpenSLES |
| + // implementation which forces us to always call Stop() and Close() before |
| + // calling Start() again. |
| SLresult err = SL_RESULT_UNKNOWN_ERROR; |
| - // Enqueues |kNumOfQueuesInBuffer| zero buffers to get the ball rolling. |
| - for (int i = 0; i < kNumOfQueuesInBuffer; ++i) { |
| + for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { |
| err = (*simple_buffer_queue_)->Enqueue( |
| - simple_buffer_queue_, |
| - audio_data_[i], |
| - buffer_size_bytes_); |
| + simple_buffer_queue_, audio_data_[i], buffer_size_bytes_); |
| if (SL_RESULT_SUCCESS != err) { |
| HandleError(err); |
| + started_ = false; |
| return; |
| } |
| } |
| - // Start the recording by setting the state to |SL_RECORDSTATE_RECORDING|. |
| + // Start the recording by setting the state to SL_RECORDSTATE_RECORDING. |
| + // When the object is in the SL_RECORDSTATE_RECORDING state, adding buffers |
| + // will implicitly start the filling process. |
| err = (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_RECORDING); |
| - if (SL_RESULT_SUCCESS != err) |
| + if (SL_RESULT_SUCCESS != err) { |
| HandleError(err); |
| + started_ = false; |
| + return; |
| + } |
| + |
| + started_ = true; |
| } |
| void OpenSLESInputStream::Stop() { |
| + DVLOG(2) << "OpenSLESInputStream::Stop()"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!started_) |
| return; |
| - // Stop recording by setting the record state to |SL_RECORDSTATE_STOPPED|. |
| + base::AutoLock lock(lock_); |
| + |
| + // Stop recording by setting the record state to SL_RECORDSTATE_STOPPED. |
| LOG_ON_FAILURE_AND_RETURN( |
| - (*recorder_)->SetRecordState(recorder_, |
| - SL_RECORDSTATE_STOPPED)); |
| + (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_STOPPED)); |
| // Clear the buffer queue to get rid of old data when resuming recording. |
| LOG_ON_FAILURE_AND_RETURN( |
| @@ -114,17 +135,30 @@ void OpenSLESInputStream::Stop() { |
| } |
| void OpenSLESInputStream::Close() { |
| + DVLOG(2) << "OpenSLESInputStream::Close()"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| // Stop the stream if it is still recording. |
| Stop(); |
| + { |
| + base::AutoLock lock(lock_); |
| - // 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; |
| + if (callback_) { |
| + callback_->OnClose(this); |
|
wjia(left Chromium)
2013/09/07 17:09:36
I don't quite understand why callback_ is used her
henrika (OOO until Aug 14)
2013/09/10 11:17:18
Neither do I but that is how it is implemented on
|
| + callback_ = NULL; |
| + } |
| + |
| + // Destroy the buffer queue recorder object and invalidate all associated |
| + // interfaces. |
| + recorder_object_.Reset(); |
| + simple_buffer_queue_ = NULL; |
| + recorder_ = NULL; |
| - ReleaseAudioBuffer(); |
| + // Destroy the engine object. We don't store any associated interface for |
| + // this object. |
| + engine_object_.Reset(); |
| + ReleaseAudioBuffer(); |
| + } |
| audio_manager_->ReleaseInputStream(this); |
| } |
| @@ -134,9 +168,7 @@ double OpenSLESInputStream::GetMaxVolume() { |
| return 0.0; |
| } |
| -void OpenSLESInputStream::SetVolume(double volume) { |
| - NOTIMPLEMENTED(); |
| -} |
| +void OpenSLESInputStream::SetVolume(double volume) { NOTIMPLEMENTED(); } |
| double OpenSLESInputStream::GetVolume() { |
| NOTIMPLEMENTED(); |
| @@ -153,54 +185,47 @@ bool OpenSLESInputStream::GetAutomaticGainControl() { |
| } |
| bool OpenSLESInputStream::CreateRecorder() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(!engine_object_.Get()); |
| + DCHECK(!recorder_object_.Get()); |
| + DCHECK(!recorder_); |
| + DCHECK(!simple_buffer_queue_); |
| + |
| // 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) } |
| - }; |
| - LOG_ON_FAILURE_AND_RETURN(slCreateEngine(engine_object_.Receive(), |
| - 1, |
| - option, |
| - 0, |
| - NULL, |
| - NULL), |
| - false); |
| + {SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE)}}; |
| + LOG_ON_FAILURE_AND_RETURN( |
| + slCreateEngine(engine_object_.Receive(), 1, option, 0, NULL, NULL), |
| + false); |
| // Realize the SL engine object in synchronous mode. |
| - LOG_ON_FAILURE_AND_RETURN(engine_object_->Realize(engine_object_.Get(), |
| - SL_BOOLEAN_FALSE), |
| - false); |
| + LOG_ON_FAILURE_AND_RETURN( |
| + engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE), false); |
| // Get the SL engine interface which is implicit. |
| SLEngineItf engine; |
| - LOG_ON_FAILURE_AND_RETURN(engine_object_->GetInterface(engine_object_.Get(), |
| - SL_IID_ENGINE, |
| - &engine), |
| + LOG_ON_FAILURE_AND_RETURN(engine_object_->GetInterface( |
| + engine_object_.Get(), SL_IID_ENGINE, &engine), |
| false); |
| // Audio source configuration. |
| SLDataLocator_IODevice mic_locator = { |
| - SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, |
| - SL_DEFAULTDEVICEID_AUDIOINPUT, NULL |
| - }; |
| - SLDataSource audio_source = { &mic_locator, NULL }; |
| + 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_ }; |
| + SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, |
| + static_cast<SLuint32>(kMaxNumOfBuffersInQueue)}; |
| + SLDataSink audio_sink = {&buffer_queue, &format_}; |
| // Create an audio recorder. |
| - const SLInterfaceID interface_id[] = { |
| - SL_IID_ANDROIDSIMPLEBUFFERQUEUE, |
| - SL_IID_ANDROIDCONFIGURATION |
| - }; |
| - const SLboolean interface_required[] = { |
| - SL_BOOLEAN_TRUE, |
| - SL_BOOLEAN_TRUE |
| - }; |
| + const SLInterfaceID interface_id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, |
| + SL_IID_ANDROIDCONFIGURATION}; |
| + const SLboolean interface_required[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; |
| + |
| // Create AudioRecorder and specify SL_IID_ANDROIDCONFIGURATION. |
| LOG_ON_FAILURE_AND_RETURN( |
| (*engine)->CreateAudioRecorder(engine, |
| @@ -219,24 +244,24 @@ bool OpenSLESInputStream::CreateRecorder() { |
| &recorder_config), |
| false); |
| + // Uses the main microphone tuned for audio communications. |
| SLint32 stream_type = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; |
| LOG_ON_FAILURE_AND_RETURN( |
| (*recorder_config)->SetConfiguration(recorder_config, |
| SL_ANDROID_KEY_RECORDING_PRESET, |
| - &stream_type, sizeof(SLint32)), |
| + &stream_type, |
| + sizeof(SLint32)), |
| false); |
| // Realize the recorder object in synchronous mode. |
| LOG_ON_FAILURE_AND_RETURN( |
| - recorder_object_->Realize(recorder_object_.Get(), |
| - SL_BOOLEAN_FALSE), |
| + recorder_object_->Realize(recorder_object_.Get(), SL_BOOLEAN_FALSE), |
| false); |
| // Get an implicit recorder interface. |
| LOG_ON_FAILURE_AND_RETURN( |
| - recorder_object_->GetInterface(recorder_object_.Get(), |
| - SL_IID_RECORD, |
| - &recorder_), |
| + recorder_object_->GetInterface( |
| + recorder_object_.Get(), SL_IID_RECORD, &recorder_), |
| false); |
| // Get the simple buffer queue interface. |
| @@ -249,61 +274,78 @@ bool OpenSLESInputStream::CreateRecorder() { |
| // Register the input callback for the simple buffer queue. |
| // This callback will be called when receiving new data from the device. |
| LOG_ON_FAILURE_AND_RETURN( |
| - (*simple_buffer_queue_)->RegisterCallback(simple_buffer_queue_, |
| - SimpleBufferQueueCallback, |
| - this), |
| + (*simple_buffer_queue_)->RegisterCallback( |
| + simple_buffer_queue_, SimpleBufferQueueCallback, this), |
| false); |
| return true; |
| } |
| void OpenSLESInputStream::SimpleBufferQueueCallback( |
| - SLAndroidSimpleBufferQueueItf buffer_queue, void* instance) { |
| + SLAndroidSimpleBufferQueueItf buffer_queue, |
| + void* instance) { |
| OpenSLESInputStream* stream = |
| reinterpret_cast<OpenSLESInputStream*>(instance); |
| stream->ReadBufferQueue(); |
| } |
| void OpenSLESInputStream::ReadBufferQueue() { |
| + base::AutoLock lock(lock_); |
| if (!started_) |
| return; |
| - // TODO(xians): Get an accurate delay estimation. |
| + TRACE_EVENT0("audio", "OpenSLESOutputStream::ReadBufferQueue"); |
| + |
| + // Verify that we are in a recording state. |
| + SLuint32 state; |
| + SLresult err = (*recorder_)->GetRecordState(recorder_, &state); |
| + if (SL_RESULT_SUCCESS != err) { |
| + HandleError(err); |
| + return; |
| + } |
| + if (state != SL_RECORDSTATE_RECORDING) { |
| + DLOG(WARNING) << "Received callback in non-recording state"; |
| + return; |
| + } |
|
wjia(left Chromium)
2013/09/07 17:09:36
The above check is not needed. OpenSLESInputStream
henrika (OOO until Aug 14)
2013/09/10 11:17:18
Done.
|
| + |
| + // TODO(henrika): Investigate if it is possible to get an accurate |
| + // delay estimation. |
| callback_->OnData(this, |
| - audio_data_[active_queue_], |
| + audio_data_[active_buffer_index_], |
| buffer_size_bytes_, |
| buffer_size_bytes_, |
| 0.0); |
| // Done with this buffer. Send it to device for recording. |
| - SLresult err = (*simple_buffer_queue_)->Enqueue( |
| - simple_buffer_queue_, |
| - audio_data_[active_queue_], |
| - buffer_size_bytes_); |
| + err = (*simple_buffer_queue_)->Enqueue(simple_buffer_queue_, |
| + audio_data_[active_buffer_index_], |
| + buffer_size_bytes_); |
| if (SL_RESULT_SUCCESS != err) |
| HandleError(err); |
| - active_queue_ = (active_queue_ + 1) % kNumOfQueuesInBuffer; |
| + active_buffer_index_ = (active_buffer_index_ + 1) % kMaxNumOfBuffersInQueue; |
| } |
| void OpenSLESInputStream::SetupAudioBuffer() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!audio_data_[0]); |
| - for (int i = 0; i < kNumOfQueuesInBuffer; ++i) { |
| + for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { |
| audio_data_[i] = new uint8[buffer_size_bytes_]; |
| } |
| } |
| void OpenSLESInputStream::ReleaseAudioBuffer() { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| if (audio_data_[0]) { |
| - for (int i = 0; i < kNumOfQueuesInBuffer; ++i) { |
| - delete [] audio_data_[i]; |
| + for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { |
| + delete[] audio_data_[i]; |
| audio_data_[i] = NULL; |
| } |
| } |
| } |
| void OpenSLESInputStream::HandleError(SLresult error) { |
| - DLOG(FATAL) << "OpenSLES Input error " << error; |
| + DLOG(ERROR) << "OpenSLES Input error " << error; |
| if (callback_) |
| callback_->OnError(this); |
| } |