| // 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/cxx17_backports.h" |
| #include "base/logging.h" |
| #include "base/trace_event/trace_event.h" |
| #include "media/audio/android/audio_manager_android.h" |
| #include "media/base/audio_bus.h" |
| |
| #define LOG_ON_FAILURE_AND_RETURN(op, ...) \ |
| do { \ |
| SLresult err = (op); \ |
| if (err != SL_RESULT_SUCCESS) { \ |
| DLOG(ERROR) << #op << " failed: " << err; \ |
| return __VA_ARGS__; \ |
| } \ |
| } while (0) |
| |
| namespace media { |
| |
| OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager, |
| const AudioParameters& params) |
| : audio_manager_(audio_manager), |
| callback_(nullptr), |
| recorder_(nullptr), |
| simple_buffer_queue_(nullptr), |
| active_buffer_index_(0), |
| buffer_size_bytes_(0), |
| started_(false), |
| audio_bus_(media::AudioBus::Create(params)), |
| no_effects_(params.effects() == AudioParameters::NO_EFFECTS) { |
| DVLOG(2) << __PRETTY_FUNCTION__; |
| DVLOG(1) << "Audio effects enabled: " << !no_effects_; |
| |
| const SampleFormat kSampleFormat = kSampleFormatS16; |
| |
| 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 = format_.containerSize = |
| SampleFormatToBitsPerChannel(kSampleFormat); |
| format_.endianness = SL_BYTEORDER_LITTLEENDIAN; |
| format_.channelMask = ChannelCountToSLESChannelMask(params.channels()); |
| |
| buffer_size_bytes_ = params.GetBytesPerBuffer(kSampleFormat); |
| hardware_delay_ = base::Seconds(params.frames_per_buffer() / |
| static_cast<double>(params.sample_rate())); |
| |
| memset(&audio_data_, 0, sizeof(audio_data_)); |
| } |
| |
| OpenSLESInputStream::~OpenSLESInputStream() { |
| DVLOG(2) << __PRETTY_FUNCTION__; |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!recorder_object_.Get()); |
| DCHECK(!engine_object_.Get()); |
| DCHECK(!recorder_); |
| DCHECK(!simple_buffer_queue_); |
| DCHECK(!audio_data_[0]); |
| } |
| |
| AudioInputStream::OpenOutcome OpenSLESInputStream::Open() { |
| DVLOG(2) << __PRETTY_FUNCTION__; |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (engine_object_.Get()) |
| return AudioInputStream::OpenOutcome::kFailed; |
| |
| if (!CreateRecorder()) |
| return AudioInputStream::OpenOutcome::kFailed; |
| |
| SetupAudioBuffer(); |
| return AudioInputStream::OpenOutcome::kSuccess; |
| } |
| |
| void OpenSLESInputStream::Start(AudioInputCallback* callback) { |
| DVLOG(2) << __PRETTY_FUNCTION__; |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(callback); |
| DCHECK(recorder_); |
| DCHECK(simple_buffer_queue_); |
| if (started_) |
| return; |
| |
| base::AutoLock lock(lock_); |
| DCHECK(!callback_ || callback_ == callback); |
| callback_ = callback; |
| 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; |
| for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { |
| err = (*simple_buffer_queue_)->Enqueue( |
| 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. |
| // 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) { |
| HandleError(err); |
| started_ = false; |
| return; |
| } |
| |
| started_ = true; |
| } |
| |
| void OpenSLESInputStream::Stop() { |
| DVLOG(2) << __PRETTY_FUNCTION__; |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (!started_) |
| return; |
| |
| 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)); |
| |
| // Clear the buffer queue to get rid of old data when resuming recording. |
| LOG_ON_FAILURE_AND_RETURN( |
| (*simple_buffer_queue_)->Clear(simple_buffer_queue_)); |
| |
| started_ = false; |
| callback_ = nullptr; |
| } |
| |
| void OpenSLESInputStream::Close() { |
| DVLOG(2) << __PRETTY_FUNCTION__; |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // Stop the stream if it is still recording. |
| Stop(); |
| { |
| // TODO(henrika): Do we need to hold the lock here? |
| base::AutoLock lock(lock_); |
| |
| // Destroy the buffer queue recorder object and invalidate all associated |
| // interfaces. |
| recorder_object_.Reset(); |
| simple_buffer_queue_ = nullptr; |
| recorder_ = nullptr; |
| |
| // Destroy the engine object. We don't store any associated interface for |
| // this object. |
| engine_object_.Reset(); |
| ReleaseAudioBuffer(); |
| } |
| |
| audio_manager_->ReleaseInputStream(this); |
| } |
| |
| double OpenSLESInputStream::GetMaxVolume() { |
| return 0.0; |
| } |
| |
| void OpenSLESInputStream::SetVolume(double volume) { |
| } |
| |
| double OpenSLESInputStream::GetVolume() { |
| return 0.0; |
| } |
| |
| bool OpenSLESInputStream::SetAutomaticGainControl(bool enabled) { |
| return false; |
| } |
| |
| bool OpenSLESInputStream::GetAutomaticGainControl() { |
| return false; |
| } |
| |
| bool OpenSLESInputStream::IsMuted() { |
| return false; |
| } |
| |
| void OpenSLESInputStream::SetOutputDeviceForAec( |
| const std::string& output_device_id) { |
| // Not supported. Do nothing. |
| } |
| |
| 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, nullptr, nullptr), |
| false); |
| |
| // Realize the SL engine object in synchronous mode. |
| 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), |
| false); |
| |
| // Audio source configuration. |
| SLDataLocator_IODevice mic_locator = {SL_DATALOCATOR_IODEVICE, |
| SL_IODEVICE_AUDIOINPUT, |
| SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr}; |
| SLDataSource audio_source = {&mic_locator, nullptr}; |
| |
| // Audio sink configuration. |
| SLDataLocator_AndroidSimpleBufferQueue buffer_queue = { |
| 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}; |
| |
| // Create AudioRecorder and specify SL_IID_ANDROIDCONFIGURATION. |
| LOG_ON_FAILURE_AND_RETURN( |
| (*engine)->CreateAudioRecorder( |
| engine, recorder_object_.Receive(), &audio_source, &audio_sink, |
| base::size(interface_id), interface_id, interface_required), |
| false); |
| |
| SLAndroidConfigurationItf recorder_config; |
| LOG_ON_FAILURE_AND_RETURN( |
| recorder_object_->GetInterface(recorder_object_.Get(), |
| SL_IID_ANDROIDCONFIGURATION, |
| &recorder_config), |
| false); |
| |
| // Uses the main microphone tuned for audio communications if effects are |
| // enabled and disables all audio processing if effects are disabled. |
| SLint32 stream_type = no_effects_ |
| ? SL_ANDROID_RECORDING_PRESET_CAMCORDER |
| : 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)), |
| false); |
| |
| // Realize the recorder object in synchronous mode. |
| LOG_ON_FAILURE_AND_RETURN( |
| 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_), |
| false); |
| |
| // Get the simple buffer queue interface. |
| LOG_ON_FAILURE_AND_RETURN( |
| recorder_object_->GetInterface(recorder_object_.Get(), |
| SL_IID_ANDROIDSIMPLEBUFFERQUEUE, |
| &simple_buffer_queue_), |
| false); |
| |
| // 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), |
| false); |
| |
| return true; |
| } |
| |
| void OpenSLESInputStream::SimpleBufferQueueCallback( |
| SLAndroidSimpleBufferQueueItf buffer_queue, |
| void* instance) { |
| OpenSLESInputStream* stream = |
| reinterpret_cast<OpenSLESInputStream*>(instance); |
| stream->ReadBufferQueue(); |
| } |
| |
| void OpenSLESInputStream::ReadBufferQueue() { |
| base::AutoLock lock(lock_); |
| if (!started_) |
| return; |
| |
| TRACE_EVENT0("audio", "OpenSLESOutputStream::ReadBufferQueue"); |
| |
| // Convert from interleaved format to deinterleaved audio bus format. |
| audio_bus_->FromInterleaved<SignedInt16SampleTypeTraits>( |
| reinterpret_cast<int16_t*>(audio_data_[active_buffer_index_]), |
| audio_bus_->frames()); |
| |
| // TODO(henrika): Investigate if it is possible to get an accurate |
| // delay estimation. |
| callback_->OnData(audio_bus_.get(), base::TimeTicks::Now() - hardware_delay_, |
| 0.0); |
| |
| // Done with this buffer. Send it to device for recording. |
| SLresult err = |
| (*simple_buffer_queue_)->Enqueue(simple_buffer_queue_, |
| audio_data_[active_buffer_index_], |
| buffer_size_bytes_); |
| if (SL_RESULT_SUCCESS != err) |
| HandleError(err); |
| |
| active_buffer_index_ = (active_buffer_index_ + 1) % kMaxNumOfBuffersInQueue; |
| } |
| |
| void OpenSLESInputStream::SetupAudioBuffer() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!audio_data_[0]); |
| for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { |
| audio_data_[i] = new uint8_t[buffer_size_bytes_]; |
| } |
| } |
| |
| void OpenSLESInputStream::ReleaseAudioBuffer() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (audio_data_[0]) { |
| for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) { |
| delete[] audio_data_[i]; |
| audio_data_[i] = nullptr; |
| } |
| } |
| } |
| |
| void OpenSLESInputStream::HandleError(SLresult error) { |
| DLOG(ERROR) << "OpenSLES Input error " << error; |
| if (callback_) |
| callback_->OnError(); |
| } |
| |
| } // namespace media |