| /* |
| * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/modules/audio_device/android/audio_device_opensles_android.h" |
| |
| #ifdef WEBRTC_ANDROID_DEBUG |
| #include <android/log.h> |
| #endif |
| #include <sys/resource.h> |
| #include <sys/syscall.h> |
| #include <sys/time.h> |
| #include <time.h> |
| |
| #include "webrtc/modules/audio_device/audio_device_utility.h" |
| #include "webrtc/system_wrappers/interface/event_wrapper.h" |
| #include "webrtc/system_wrappers/interface/thread_wrapper.h" |
| #include "webrtc/system_wrappers/interface/trace.h" |
| |
| #ifdef WEBRTC_ANDROID_DEBUG |
| #define WEBRTC_OPENSL_TRACE(a, b, c, ...) \ |
| __android_log_print(ANDROID_LOG_DEBUG, "WebRTC OpenSLES", __VA_ARGS__) |
| #else |
| #define WEBRTC_OPENSL_TRACE WEBRTC_TRACE |
| #endif |
| |
| namespace webrtc { |
| |
| AudioDeviceAndroidOpenSLES::AudioDeviceAndroidOpenSLES(const int32_t id) |
| : voe_audio_buffer_(NULL), |
| crit_sect_(*CriticalSectionWrapper::CreateCriticalSection()), |
| id_(id), |
| sles_engine_(NULL), |
| sles_player_(NULL), |
| sles_engine_itf_(NULL), |
| sles_player_itf_(NULL), |
| sles_player_sbq_itf_(NULL), |
| sles_output_mixer_(NULL), |
| sles_speaker_volume_(NULL), |
| sles_recorder_(NULL), |
| sles_recorder_itf_(NULL), |
| sles_recorder_sbq_itf_(NULL), |
| sles_mic_volume_(NULL), |
| mic_dev_id_(0), |
| play_warning_(0), |
| play_error_(0), |
| rec_warning_(0), |
| rec_error_(0), |
| is_recording_dev_specified_(false), |
| is_playout_dev_specified_(false), |
| is_initialized_(false), |
| is_recording_(false), |
| is_playing_(false), |
| is_rec_initialized_(false), |
| is_play_initialized_(false), |
| is_mic_initialized_(false), |
| is_speaker_initialized_(false), |
| playout_delay_(0), |
| recording_delay_(0), |
| agc_enabled_(false), |
| rec_timer_(*EventWrapper::Create()), |
| mic_sampling_rate_(N_REC_SAMPLES_PER_SEC * 1000), |
| speaker_sampling_rate_(N_PLAY_SAMPLES_PER_SEC * 1000), |
| max_speaker_vol_(0), |
| min_speaker_vol_(0), |
| loundspeaker_on_(false) { |
| WEBRTC_OPENSL_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", |
| __FUNCTION__); |
| memset(rec_buf_, 0, sizeof(rec_buf_)); |
| memset(play_buf_, 0, sizeof(play_buf_)); |
| } |
| |
| AudioDeviceAndroidOpenSLES::~AudioDeviceAndroidOpenSLES() { |
| WEBRTC_OPENSL_TRACE(kTraceMemory, kTraceAudioDevice, id_, "%s destroyed", |
| __FUNCTION__); |
| |
| Terminate(); |
| |
| delete &crit_sect_; |
| delete &rec_timer_; |
| } |
| |
| void AudioDeviceAndroidOpenSLES::AttachAudioBuffer( |
| AudioDeviceBuffer* audioBuffer) { |
| |
| CriticalSectionScoped lock(&crit_sect_); |
| |
| voe_audio_buffer_ = audioBuffer; |
| |
| // Inform the AudioBuffer about default settings for this implementation. |
| voe_audio_buffer_->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC); |
| voe_audio_buffer_->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC); |
| voe_audio_buffer_->SetRecordingChannels(N_REC_CHANNELS); |
| voe_audio_buffer_->SetPlayoutChannels(N_PLAY_CHANNELS); |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::ActiveAudioLayer( |
| AudioDeviceModule::AudioLayer& audioLayer) const { |
| |
| audioLayer = AudioDeviceModule::kPlatformDefaultAudio; |
| |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::Init() { |
| CriticalSectionScoped lock(&crit_sect_); |
| |
| if (is_initialized_) |
| return 0; |
| |
| SLEngineOption EngineOption[] = { |
| { SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE) }, |
| }; |
| int32_t res = slCreateEngine(&sles_engine_, 1, EngineOption, 0, NULL, NULL); |
| |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to create SL Engine Object"); |
| return -1; |
| } |
| |
| // Realizing the SL Engine in synchronous mode. |
| if ((*sles_engine_)->Realize(sles_engine_, SL_BOOLEAN_FALSE) |
| != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to Realize SL Engine"); |
| return -1; |
| } |
| |
| if ((*sles_engine_)->GetInterface( |
| sles_engine_, |
| SL_IID_ENGINE, |
| &sles_engine_itf_) != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to get SL Engine interface"); |
| return -1; |
| } |
| |
| // Check the sample rate to be used for playback and recording |
| if (InitSampleRate() != 0) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| "%s: Failed to init samplerate", __FUNCTION__); |
| return -1; |
| } |
| |
| // Set the audio device buffer sampling rate, we assume we get the same |
| // for play and record. |
| if (voe_audio_buffer_->SetRecordingSampleRate(mic_sampling_rate_) < 0) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Could not set mic audio device buffer " |
| "sampling rate (%d)", mic_sampling_rate_); |
| } |
| if (voe_audio_buffer_->SetPlayoutSampleRate(speaker_sampling_rate_) < 0) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Could not set speaker audio device buffer " |
| "sampling rate (%d)", speaker_sampling_rate_); |
| } |
| |
| is_initialized_ = true; |
| |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::Terminate() { |
| CriticalSectionScoped lock(&crit_sect_); |
| |
| if (!is_initialized_) |
| return 0; |
| |
| // RECORDING |
| StopRecording(); |
| |
| is_mic_initialized_ = false; |
| is_recording_dev_specified_ = false; |
| |
| // PLAYOUT |
| StopPlayout(); |
| |
| if (sles_engine_ != NULL) { |
| (*sles_engine_)->Destroy(sles_engine_); |
| sles_engine_ = NULL; |
| sles_engine_itf_ = NULL; |
| } |
| |
| is_initialized_ = false; |
| return 0; |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::Initialized() const { |
| return (is_initialized_); |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SpeakerIsAvailable( |
| bool& available) { |
| // We always assume it's available |
| available = true; |
| |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::InitSpeaker() { |
| CriticalSectionScoped lock(&crit_sect_); |
| |
| if (is_playing_) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " Playout already started"); |
| return -1; |
| } |
| |
| if (!is_playout_dev_specified_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Playout device is not specified"); |
| return -1; |
| } |
| |
| // Nothing needs to be done here, we use a flag to have consistent |
| // behavior with other platforms. |
| is_speaker_initialized_ = true; |
| |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MicrophoneIsAvailable( |
| bool& available) { |
| // We always assume it's available. |
| available = true; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::InitMicrophone() { |
| CriticalSectionScoped lock(&crit_sect_); |
| if (is_recording_) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " Recording already started"); |
| return -1; |
| } |
| if (!is_recording_dev_specified_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Recording device is not specified"); |
| return -1; |
| } |
| |
| // Nothing needs to be done here, we use a flag to have consistent |
| // behavior with other platforms. |
| is_mic_initialized_ = true; |
| return 0; |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::SpeakerIsInitialized() const { |
| return is_speaker_initialized_; |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::MicrophoneIsInitialized() const { |
| return is_mic_initialized_; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SpeakerVolumeIsAvailable( |
| bool& available) { |
| available = true; // We assume we are always be able to set/get volume. |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetSpeakerVolume( |
| uint32_t volume) { |
| if (!is_speaker_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Speaker not initialized"); |
| return -1; |
| } |
| |
| if (sles_engine_ == NULL) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| "SetSpeakerVolume, SL Engine object doesnt exist"); |
| return -1; |
| } |
| |
| if (sles_engine_itf_ == NULL) { |
| if ((*sles_engine_)->GetInterface( |
| sles_engine_, |
| SL_IID_ENGINE, |
| &sles_engine_itf_) != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to GetInterface SL Engine Interface"); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SpeakerVolume( |
| uint32_t& volume) const { |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetWaveOutVolume( |
| uint16_t volumeLeft, |
| uint16_t volumeRight) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::WaveOutVolume( |
| uint16_t& volumeLeft, |
| uint16_t& volumeRight) const { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MaxSpeakerVolume( |
| uint32_t& maxVolume) const { |
| if (!is_speaker_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Speaker not initialized"); |
| return -1; |
| } |
| |
| maxVolume = max_speaker_vol_; |
| |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MinSpeakerVolume( |
| uint32_t& minVolume) const { |
| if (!is_speaker_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Speaker not initialized"); |
| return -1; |
| } |
| minVolume = min_speaker_vol_; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SpeakerVolumeStepSize( |
| uint16_t& stepSize) const { |
| if (!is_speaker_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Speaker not initialized"); |
| return -1; |
| } |
| stepSize = 1; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SpeakerMuteIsAvailable( |
| bool& available) { |
| available = false; // Speaker mute not supported on Android. |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetSpeakerMute(bool enable) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SpeakerMute( |
| bool& enabled) const { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MicrophoneMuteIsAvailable( |
| bool& available) { |
| available = false; // Mic mute not supported on Android |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetMicrophoneMute(bool enable) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MicrophoneMute( |
| bool& enabled) const { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MicrophoneBoostIsAvailable( |
| bool& available) { |
| available = false; // Mic boost not supported on Android. |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetMicrophoneBoost(bool enable) { |
| if (!is_mic_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Microphone not initialized"); |
| return -1; |
| } |
| if (enable) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Enabling not available"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MicrophoneBoost( |
| bool& enabled) const { |
| if (!is_mic_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Microphone not initialized"); |
| return -1; |
| } |
| enabled = false; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::StereoRecordingIsAvailable( |
| bool& available) { |
| available = false; // Stereo recording not supported on Android. |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetStereoRecording(bool enable) { |
| if (enable) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Enabling not available"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::StereoRecording( |
| bool& enabled) const { |
| enabled = false; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::StereoPlayoutIsAvailable( |
| bool& available) { |
| // TODO(leozwang): This api is called before initplayout, we need |
| // to detect audio device to find out if stereo is supported or not. |
| available = false; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetStereoPlayout(bool enable) { |
| if (enable) { |
| return 0; |
| } else { |
| // TODO(leozwang): Enforce mono. |
| return 0; |
| } |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::StereoPlayout( |
| bool& enabled) const { |
| enabled = (player_pcm_.numChannels == 2 ? true : false); |
| return 0; |
| } |
| |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetAGC(bool enable) { |
| agc_enabled_ = enable; |
| return 0; |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::AGC() const { |
| return agc_enabled_; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MicrophoneVolumeIsAvailable( |
| bool& available) { |
| available = true; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetMicrophoneVolume( |
| uint32_t volume) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " OpenSL doesn't support contolling Mic volume yet"); |
| // TODO(leozwang): Add microphone volume control when OpenSL apis |
| // are available. |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MicrophoneVolume( |
| uint32_t& volume) const { |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MaxMicrophoneVolume( |
| uint32_t& maxVolume) const { |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MinMicrophoneVolume( |
| uint32_t& minVolume) const { |
| minVolume = 0; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::MicrophoneVolumeStepSize( |
| uint16_t& stepSize) const { |
| stepSize = 1; |
| return 0; |
| } |
| |
| int16_t AudioDeviceAndroidOpenSLES::PlayoutDevices() { |
| return 1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetPlayoutDevice( |
| uint16_t index) { |
| if (is_play_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Playout already initialized"); |
| return -1; |
| } |
| if (0 != index) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Device index is out of range [0,0]"); |
| return -1; |
| } |
| |
| // Do nothing but set a flag, this is to have consistent behaviour |
| // with other platforms. |
| is_playout_dev_specified_ = true; |
| |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetPlayoutDevice( |
| AudioDeviceModule::WindowsDeviceType device) { |
| |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::PlayoutDeviceName( |
| uint16_t index, |
| char name[kAdmMaxDeviceNameSize], |
| char guid[kAdmMaxGuidSize]) { |
| if (0 != index) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Device index is out of range [0,0]"); |
| return -1; |
| } |
| |
| // Return empty string |
| memset(name, 0, kAdmMaxDeviceNameSize); |
| |
| if (guid) { |
| memset(guid, 0, kAdmMaxGuidSize); |
| } |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::RecordingDeviceName( |
| uint16_t index, |
| char name[kAdmMaxDeviceNameSize], |
| char guid[kAdmMaxGuidSize]) { |
| if (0 != index) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Device index is out of range [0,0]"); |
| return -1; |
| } |
| |
| // Return empty string |
| memset(name, 0, kAdmMaxDeviceNameSize); |
| |
| if (guid) { |
| memset(guid, 0, kAdmMaxGuidSize); |
| } |
| return 0; |
| } |
| |
| int16_t AudioDeviceAndroidOpenSLES::RecordingDevices() { |
| return 1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetRecordingDevice( |
| uint16_t index) { |
| if (is_rec_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Recording already initialized"); |
| return -1; |
| } |
| |
| if (0 != index) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Device index is out of range [0,0]"); |
| return -1; |
| } |
| |
| // Do nothing but set a flag, this is to have consistent behaviour with |
| // other platforms. |
| is_recording_dev_specified_ = true; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetRecordingDevice( |
| AudioDeviceModule::WindowsDeviceType device) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::PlayoutIsAvailable( |
| bool& available) { |
| available = false; |
| int32_t res = InitPlayout(); |
| StopPlayout(); |
| if (res != -1) { |
| available = true; |
| } |
| return res; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::RecordingIsAvailable( |
| bool& available) { |
| available = false; |
| int32_t res = InitRecording(); |
| StopRecording(); |
| if (res != -1) { |
| available = true; |
| } |
| return res; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::InitPlayout() { |
| CriticalSectionScoped lock(&crit_sect_); |
| if (!is_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Not initialized"); |
| return -1; |
| } |
| |
| if (is_playing_) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " Playout already started"); |
| return -1; |
| } |
| |
| if (!is_playout_dev_specified_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Playout device is not specified"); |
| return -1; |
| } |
| |
| if (is_play_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceInfo, kTraceAudioDevice, id_, |
| " Playout already initialized"); |
| return 0; |
| } |
| |
| // Initialize the speaker |
| if (InitSpeaker() == -1) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " InitSpeaker() failed"); |
| } |
| |
| if (sles_engine_ == NULL || sles_engine_itf_ == NULL) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " SLObject or Engiine is NULL"); |
| return -1; |
| } |
| |
| SLDataLocator_AndroidSimpleBufferQueue simple_buf_queue = { |
| SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, |
| static_cast<SLuint32>(N_PLAY_QUEUE_BUFFERS) |
| }; |
| SLDataSource audio_source = { &simple_buf_queue, &player_pcm_ }; |
| SLDataLocator_OutputMix locator_outputmix; |
| SLDataSink audio_sink = { &locator_outputmix, NULL }; |
| |
| // Create Output Mix object to be used by player. |
| int32_t res = -1; |
| res = (*sles_engine_itf_)->CreateOutputMix(sles_engine_itf_, |
| &sles_output_mixer_, |
| 0, |
| NULL, |
| NULL); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to get SL Output Mix object"); |
| return -1; |
| } |
| // Realizing the Output Mix object in synchronous mode. |
| res = (*sles_output_mixer_)->Realize(sles_output_mixer_, SL_BOOLEAN_FALSE); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to realize SL Output Mix object"); |
| return -1; |
| } |
| |
| // The code below can be moved to startplayout instead |
| // Setup the data source structure for the buffer queue. |
| player_pcm_.formatType = SL_DATAFORMAT_PCM; |
| player_pcm_.numChannels = N_PLAY_CHANNELS; |
| if (speaker_sampling_rate_ == 44000) { |
| player_pcm_.samplesPerSec = 44100 * 1000; |
| } else { |
| player_pcm_.samplesPerSec = speaker_sampling_rate_ * 1000; |
| } |
| player_pcm_.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; |
| player_pcm_.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; |
| if (1 == player_pcm_.numChannels) { |
| player_pcm_.channelMask = SL_SPEAKER_FRONT_CENTER; |
| } else if (2 == player_pcm_.numChannels) { |
| player_pcm_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; |
| } else { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " %d player channels not supported", N_PLAY_CHANNELS); |
| } |
| |
| player_pcm_.endianness = SL_BYTEORDER_LITTLEENDIAN; |
| // Setup the data sink structure. |
| locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; |
| locator_outputmix.outputMix = sles_output_mixer_; |
| |
| SLInterfaceID ids[N_MAX_INTERFACES] = { |
| SL_IID_BUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; |
| SLboolean req[N_MAX_INTERFACES] = { |
| SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; |
| res = (*sles_engine_itf_)->CreateAudioPlayer(sles_engine_itf_, |
| &sles_player_, &audio_source, |
| &audio_sink, 2, ids, req); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to create AudioPlayer"); |
| return -1; |
| } |
| |
| // Realizing the player in synchronous mode. |
| res = (*sles_player_)->Realize(sles_player_, SL_BOOLEAN_FALSE); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to realize the player"); |
| return -1; |
| } |
| res = (*sles_player_)->GetInterface( |
| sles_player_, SL_IID_PLAY, |
| static_cast<void*>(&sles_player_itf_)); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to get Player interface"); |
| return -1; |
| } |
| res = (*sles_player_)->GetInterface( |
| sles_player_, SL_IID_BUFFERQUEUE, |
| static_cast<void*>(&sles_player_sbq_itf_)); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to get Player SimpleBufferQueue interface"); |
| return -1; |
| } |
| |
| // Setup to receive buffer queue event callbacks |
| res = (*sles_player_sbq_itf_)->RegisterCallback( |
| sles_player_sbq_itf_, |
| PlayerSimpleBufferQueueCallback, |
| this); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to register Player Callback"); |
| return -1; |
| } |
| is_play_initialized_ = true; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::InitRecording() { |
| CriticalSectionScoped lock(&crit_sect_); |
| |
| if (!is_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Not initialized"); |
| return -1; |
| } |
| |
| if (is_recording_) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " Recording already started"); |
| return -1; |
| } |
| |
| if (!is_recording_dev_specified_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Recording device is not specified"); |
| return -1; |
| } |
| |
| if (is_rec_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceInfo, kTraceAudioDevice, id_, |
| " Recording already initialized"); |
| return 0; |
| } |
| |
| // Initialize the microphone |
| if (InitMicrophone() == -1) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " InitMicrophone() failed"); |
| } |
| |
| if (sles_engine_ == NULL || sles_engine_itf_ == NULL) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Recording object is NULL"); |
| return -1; |
| } |
| |
| SLDataLocator_IODevice micLocator = { |
| SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, |
| SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; |
| SLDataSource audio_source = { &micLocator, NULL }; |
| SLDataLocator_AndroidSimpleBufferQueue simple_buf_queue = { |
| SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, |
| static_cast<SLuint32>(N_REC_QUEUE_BUFFERS) |
| }; |
| SLDataSink audio_sink = { &simple_buf_queue, &record_pcm_ }; |
| |
| // Setup the format of the content in the buffer queue |
| record_pcm_.formatType = SL_DATAFORMAT_PCM; |
| record_pcm_.numChannels = N_REC_CHANNELS; |
| if (mic_sampling_rate_ == 44000) { |
| record_pcm_.samplesPerSec = 44100 * 1000; |
| } else { |
| record_pcm_.samplesPerSec = mic_sampling_rate_ * 1000; |
| } |
| record_pcm_.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; |
| record_pcm_.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; |
| if (1 == record_pcm_.numChannels) { |
| record_pcm_.channelMask = SL_SPEAKER_FRONT_CENTER; |
| } else if (2 == record_pcm_.numChannels) { |
| record_pcm_.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; |
| } else { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " %d rec channels not supported", N_REC_CHANNELS); |
| } |
| record_pcm_.endianness = SL_BYTEORDER_LITTLEENDIAN; |
| |
| const SLInterfaceID id[2] = { |
| SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; |
| const SLboolean req[2] = { |
| SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; |
| int32_t res = -1; |
| res = (*sles_engine_itf_)->CreateAudioRecorder(sles_engine_itf_, |
| &sles_recorder_, |
| &audio_source, |
| &audio_sink, |
| 2, |
| id, |
| req); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to create Recorder"); |
| return -1; |
| } |
| |
| // Realizing the recorder in synchronous mode. |
| res = (*sles_recorder_)->Realize(sles_recorder_, SL_BOOLEAN_FALSE); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to realize Recorder"); |
| return -1; |
| } |
| |
| // Get the RECORD interface - it is an implicit interface |
| res = (*sles_recorder_)->GetInterface( |
| sles_recorder_, SL_IID_RECORD, |
| static_cast<void*>(&sles_recorder_itf_)); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to get Recorder interface"); |
| return -1; |
| } |
| |
| // Get the simpleBufferQueue interface |
| res = (*sles_recorder_)->GetInterface( |
| sles_recorder_, |
| SL_IID_ANDROIDSIMPLEBUFFERQUEUE, |
| static_cast<void*>(&sles_recorder_sbq_itf_)); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to get Recorder Simple Buffer Queue"); |
| return -1; |
| } |
| |
| // Setup to receive buffer queue event callbacks |
| res = (*sles_recorder_sbq_itf_)->RegisterCallback( |
| sles_recorder_sbq_itf_, |
| RecorderSimpleBufferQueueCallback, |
| this); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to register Recorder Callback"); |
| return -1; |
| } |
| |
| is_rec_initialized_ = true; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::StartRecording() { |
| CriticalSectionScoped lock(&crit_sect_); |
| |
| if (!is_rec_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Recording not initialized"); |
| return -1; |
| } |
| |
| if (is_recording_) { |
| WEBRTC_OPENSL_TRACE(kTraceInfo, kTraceAudioDevice, id_, |
| " Recording already started"); |
| return 0; |
| } |
| |
| if (sles_recorder_itf_ == NULL) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " RecordITF is NULL"); |
| return -1; |
| } |
| |
| if (sles_recorder_sbq_itf_ == NULL) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Recorder Simple Buffer Queue is NULL"); |
| return -1; |
| } |
| |
| memset(rec_buf_, 0, sizeof(rec_buf_)); |
| memset(rec_voe_buf_, 0, sizeof(rec_voe_buf_)); |
| uint32_t num_bytes = |
| N_REC_CHANNELS * sizeof(int16_t) * mic_sampling_rate_ / 100; |
| |
| while (!rec_queue_.empty()) |
| rec_queue_.pop(); |
| while (!rec_voe_audio_queue_.empty()) |
| rec_voe_audio_queue_.pop(); |
| while (!rec_voe_ready_queue_.empty()) |
| rec_voe_ready_queue_.pop(); |
| |
| for (int i = 0; i < N_REC_QUEUE_BUFFERS; ++i) { |
| rec_voe_ready_queue_.push(rec_voe_buf_[i]); |
| } |
| |
| int32_t res = -1; |
| for (int i = 0; i < N_REC_QUEUE_BUFFERS; ++i) { |
| // We assign 10ms buffer to each queue, size given in bytes. |
| res = (*sles_recorder_sbq_itf_)->Enqueue( |
| sles_recorder_sbq_itf_, |
| static_cast<void*>(rec_buf_[i]), |
| num_bytes); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| "Recorder Enqueue failed:%d,%d", i, res); |
| break; |
| } else { |
| rec_queue_.push(rec_buf_[i]); |
| } |
| } |
| |
| // Record the audio |
| res = (*sles_recorder_itf_)->SetRecordState(sles_recorder_itf_, |
| SL_RECORDSTATE_RECORDING); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to start recording"); |
| return -1; |
| } |
| |
| // Start rec thread and playout thread |
| rec_thread_ = ThreadWrapper::CreateThread( |
| RecThreadFunc, |
| this, |
| kRealtimePriority, |
| "opensl_capture_thread"); |
| if (rec_thread_ == NULL) { |
| WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, id_, |
| " failed to create the rec audio thread"); |
| return -1; |
| } |
| |
| unsigned int thread_id = 0; |
| if (!rec_thread_->Start(thread_id)) { |
| WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, id_, |
| " failed to start the rec audio thread"); |
| delete rec_thread_; |
| rec_thread_ = NULL; |
| return -1; |
| } |
| rec_thread_id_ = thread_id; |
| |
| is_recording_ = true; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::StopRecording() { |
| // Stop the recording thread |
| // Cannot be under lock, risk of deadlock |
| if (rec_thread_) { |
| if (rec_thread_->Stop()) { |
| delete rec_thread_; |
| rec_thread_ = NULL; |
| } else { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, id_, |
| "Failed to stop recording thread "); |
| } |
| } |
| |
| CriticalSectionScoped lock(&crit_sect_); |
| |
| if (!is_rec_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceInfo, kTraceAudioDevice, id_, |
| " Recording is not initialized"); |
| return 0; |
| } |
| |
| int32_t res = (*sles_recorder_itf_)->SetRecordState( |
| sles_recorder_itf_, |
| SL_RECORDSTATE_STOPPED); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to stop recording"); |
| } |
| |
| res = (*sles_recorder_sbq_itf_)->Clear(sles_recorder_sbq_itf_); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to clear recorder buffer queue"); |
| } |
| |
| // Destroy the recorder object |
| (*sles_recorder_)->Destroy(sles_recorder_); |
| |
| is_rec_initialized_ = false; |
| is_recording_ = false; |
| rec_warning_ = 0; |
| rec_error_ = 0; |
| sles_recorder_ = NULL; |
| sles_recorder_itf_ = NULL; |
| sles_recorder_sbq_itf_ = NULL; |
| |
| return 0; |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::RecordingIsInitialized() const { |
| return is_rec_initialized_; |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::Recording() const { |
| return is_recording_; |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::PlayoutIsInitialized() const { |
| return is_play_initialized_; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::StartPlayout() { |
| int i; |
| CriticalSectionScoped lock(&crit_sect_); |
| |
| if (!is_play_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Playout not initialized"); |
| return -1; |
| } |
| |
| if (is_playing_) { |
| WEBRTC_OPENSL_TRACE(kTraceInfo, kTraceAudioDevice, id_, |
| " Playout already started"); |
| return 0; |
| } |
| |
| if (sles_player_itf_ == NULL) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " PlayItf is NULL"); |
| return -1; |
| } |
| if (sles_player_sbq_itf_ == NULL) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " PlayerSimpleBufferQueue is NULL"); |
| return -1; |
| } |
| |
| uint32_t num_bytes = |
| N_PLAY_CHANNELS * sizeof(int16_t) * speaker_sampling_rate_ / 100; |
| |
| memset(play_buf_, 0, sizeof(play_buf_)); |
| |
| while (!play_queue_.empty()) |
| play_queue_.pop(); |
| |
| int32_t res = -1; |
| for (i = 0; i < std::min(2, static_cast<int>(N_PLAY_QUEUE_BUFFERS)); ++i) { |
| res = (*sles_player_sbq_itf_)->Enqueue( |
| sles_player_sbq_itf_, |
| static_cast<void*>(play_buf_[i]), |
| num_bytes); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " player simpler buffer Enqueue failed:%d,%d", |
| i, res); |
| break; |
| } else { |
| play_queue_.push(play_buf_[i]); |
| } |
| } |
| |
| res = (*sles_player_itf_)->SetPlayState( |
| sles_player_itf_, SL_PLAYSTATE_PLAYING); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to start playout"); |
| return -1; |
| } |
| |
| play_warning_ = 0; |
| play_error_ = 0; |
| is_playing_ = true; |
| |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::StopPlayout() { |
| |
| CriticalSectionScoped lock(&crit_sect_); |
| if (!is_play_initialized_) { |
| WEBRTC_OPENSL_TRACE(kTraceInfo, kTraceAudioDevice, id_, |
| " Playout is not initialized"); |
| return 0; |
| } |
| |
| if (sles_player_itf_) { |
| // Make sure player is stopped |
| int32_t res = (*sles_player_itf_)->SetPlayState(sles_player_itf_, |
| SL_PLAYSTATE_STOPPED); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to stop playout"); |
| } |
| } |
| |
| if (sles_player_sbq_itf_) { |
| int32_t res = (*sles_player_sbq_itf_)->Clear(sles_player_sbq_itf_); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " failed to clear player buffer queue"); |
| } |
| } |
| |
| if (sles_player_) { |
| // Destroy the player |
| (*sles_player_)->Destroy(sles_player_); |
| } |
| |
| if (sles_output_mixer_) { |
| // Destroy Output Mix object |
| (*sles_output_mixer_)->Destroy(sles_output_mixer_); |
| } |
| |
| is_play_initialized_ = false; |
| is_playing_ = false; |
| play_warning_ = 0; |
| play_error_ = 0; |
| sles_player_sbq_itf_ = NULL; |
| sles_player_itf_ = NULL; |
| sles_player_ = NULL; |
| sles_output_mixer_ = NULL; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::PlayoutDelay( |
| uint16_t& delayMS) const { |
| delayMS = playout_delay_; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::RecordingDelay( |
| uint16_t& delayMS) const { |
| delayMS = recording_delay_; |
| return 0; |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::Playing() const { |
| return is_playing_; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetPlayoutBuffer( |
| const AudioDeviceModule::BufferType type, |
| uint16_t sizeMS) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::PlayoutBuffer( |
| AudioDeviceModule::BufferType& type, |
| uint16_t& sizeMS) const { |
| type = AudioDeviceModule::kAdaptiveBufferSize; |
| sizeMS = playout_delay_; // Set to current playout delay |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::CPULoad( |
| uint16_t& load) const { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " API call not supported on this platform"); |
| return -1; |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::PlayoutWarning() const { |
| return (play_warning_ > 0); |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::PlayoutError() const { |
| return (play_error_ > 0); |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::RecordingWarning() const { |
| return (rec_warning_ > 0); |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::RecordingError() const { |
| return (rec_error_ > 0); |
| } |
| |
| void AudioDeviceAndroidOpenSLES::ClearPlayoutWarning() { |
| play_warning_ = 0; |
| } |
| |
| void AudioDeviceAndroidOpenSLES::ClearPlayoutError() { |
| play_error_ = 0; |
| } |
| |
| void AudioDeviceAndroidOpenSLES::ClearRecordingWarning() { |
| rec_warning_ = 0; |
| } |
| |
| void AudioDeviceAndroidOpenSLES::ClearRecordingError() { |
| rec_error_ = 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::SetLoudspeakerStatus(bool enable) { |
| loundspeaker_on_ = enable; |
| return 0; |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::GetLoudspeakerStatus( |
| bool& enabled) const { |
| enabled = loundspeaker_on_; |
| return 0; |
| } |
| |
| void AudioDeviceAndroidOpenSLES::PlayerSimpleBufferQueueCallback( |
| SLAndroidSimpleBufferQueueItf queue_itf, |
| void* p_context) { |
| AudioDeviceAndroidOpenSLES* audio_device = |
| static_cast<AudioDeviceAndroidOpenSLES*> (p_context); |
| audio_device->PlayerSimpleBufferQueueCallbackHandler(queue_itf); |
| } |
| |
| void AudioDeviceAndroidOpenSLES::PlayerSimpleBufferQueueCallbackHandler( |
| SLAndroidSimpleBufferQueueItf queue_itf) { |
| if (is_playing_) { |
| const unsigned int num_samples = speaker_sampling_rate_ / 100; |
| const unsigned int num_bytes = |
| N_PLAY_CHANNELS * num_samples * sizeof(int16_t); |
| int8_t buf[PLAY_MAX_TEMP_BUF_SIZE_PER_10ms]; |
| int8_t* audio; |
| |
| audio = play_queue_.front(); |
| play_queue_.pop(); |
| |
| int num_out = voe_audio_buffer_->RequestPlayoutData(num_samples); |
| num_out = voe_audio_buffer_->GetPlayoutData(buf); |
| if (num_samples != static_cast<unsigned int>(num_out)) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| "num (%u) != num_out (%d)", num_samples, num_out); |
| play_warning_ = 1; |
| } |
| memcpy(audio, buf, num_bytes); |
| UpdatePlayoutDelay(num_out); |
| |
| int res = (*queue_itf)->Enqueue(queue_itf, |
| audio, |
| num_bytes); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " player callback Enqueue failed, %d", res); |
| play_warning_ = 1; |
| } else { |
| play_queue_.push(audio); |
| } |
| } |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::RecThreadFunc(void* context) { |
| return (static_cast<AudioDeviceAndroidOpenSLES*>( |
| context)->RecThreadFuncImpl()); |
| } |
| |
| void AudioDeviceAndroidOpenSLES::RecorderSimpleBufferQueueCallback( |
| SLAndroidSimpleBufferQueueItf queue_itf, |
| void* p_context) { |
| AudioDeviceAndroidOpenSLES* audio_device = |
| static_cast<AudioDeviceAndroidOpenSLES*>(p_context); |
| audio_device->RecorderSimpleBufferQueueCallbackHandler(queue_itf); |
| } |
| |
| bool AudioDeviceAndroidOpenSLES::RecThreadFuncImpl() { |
| if (is_recording_) { |
| // TODO(leozwang): Add seting correct scheduling and thread priority. |
| |
| const unsigned int num_samples = mic_sampling_rate_ / 100; |
| const unsigned int num_bytes = |
| N_REC_CHANNELS * num_samples * sizeof(int16_t); |
| const unsigned int total_bytes = num_bytes; |
| int8_t buf[REC_MAX_TEMP_BUF_SIZE_PER_10ms]; |
| |
| { |
| CriticalSectionScoped lock(&crit_sect_); |
| if (rec_voe_audio_queue_.size() <= 0) { |
| rec_timer_.Wait(1); |
| return true; |
| } |
| |
| int8_t* audio = rec_voe_audio_queue_.front(); |
| rec_voe_audio_queue_.pop(); |
| memcpy(buf, audio, total_bytes); |
| memset(audio, 0, total_bytes); |
| rec_voe_ready_queue_.push(audio); |
| } |
| |
| UpdateRecordingDelay(); |
| voe_audio_buffer_->SetRecordedBuffer(buf, num_samples); |
| voe_audio_buffer_->SetVQEData(playout_delay_, recording_delay_, 0); |
| voe_audio_buffer_->DeliverRecordedData(); |
| } |
| |
| return true; |
| } |
| |
| void AudioDeviceAndroidOpenSLES::RecorderSimpleBufferQueueCallbackHandler( |
| SLAndroidSimpleBufferQueueItf queue_itf) { |
| if (is_recording_) { |
| const unsigned int num_samples = mic_sampling_rate_ / 100; |
| const unsigned int num_bytes = |
| N_REC_CHANNELS * num_samples * sizeof(int16_t); |
| const unsigned int total_bytes = num_bytes; |
| int8_t* audio; |
| |
| { |
| CriticalSectionScoped lock(&crit_sect_); |
| audio = rec_queue_.front(); |
| rec_queue_.pop(); |
| rec_voe_audio_queue_.push(audio); |
| |
| if (rec_voe_ready_queue_.size() <= 0) { |
| // Log Error. |
| rec_error_ = 1; |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " Audio Rec thread buffers underrun"); |
| } else { |
| audio = rec_voe_ready_queue_.front(); |
| rec_voe_ready_queue_.pop(); |
| } |
| } |
| |
| int32_t res = (*queue_itf)->Enqueue(queue_itf, |
| audio, |
| total_bytes); |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceWarning, kTraceAudioDevice, id_, |
| " recorder callback Enqueue failed, %d", res); |
| rec_warning_ = 1; |
| return; |
| } else { |
| rec_queue_.push(audio); |
| } |
| |
| // TODO(leozwang): OpenSL ES doesn't support AudioRecorder |
| // volume control now, add it when it's ready. |
| } |
| } |
| |
| void AudioDeviceAndroidOpenSLES::CheckErr(SLresult res) { |
| if (res != SL_RESULT_SUCCESS) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " AudioDeviceAndroidOpenSLES::CheckErr(%d)", res); |
| exit(-1); |
| } |
| } |
| |
| void AudioDeviceAndroidOpenSLES::UpdatePlayoutDelay( |
| uint32_t nSamplePlayed) { |
| // TODO(leozwang): Add accurate delay estimat. |
| playout_delay_ = (N_PLAY_QUEUE_BUFFERS - 0.5) * 10 + |
| N_PLAY_QUEUE_BUFFERS * nSamplePlayed / (speaker_sampling_rate_ / 1000); |
| } |
| |
| void AudioDeviceAndroidOpenSLES::UpdateRecordingDelay() { |
| // TODO(leozwang): Add accurate delay estimat. |
| recording_delay_ = 10; |
| const uint32_t noSamp10ms = mic_sampling_rate_ / 100; |
| recording_delay_ += (N_REC_QUEUE_BUFFERS * noSamp10ms) / |
| (mic_sampling_rate_ / 1000); |
| } |
| |
| int32_t AudioDeviceAndroidOpenSLES::InitSampleRate() { |
| if (sles_engine_ == NULL) { |
| WEBRTC_OPENSL_TRACE(kTraceError, kTraceAudioDevice, id_, |
| " SL Object is NULL"); |
| return -1; |
| } |
| |
| mic_sampling_rate_ = N_REC_SAMPLES_PER_SEC; |
| speaker_sampling_rate_ = N_PLAY_SAMPLES_PER_SEC; |
| |
| WEBRTC_OPENSL_TRACE(kTraceStateInfo, kTraceAudioDevice, id_, |
| " mic sample rate (%d), speaker sample rate (%d)", |
| mic_sampling_rate_, speaker_sampling_rate_); |
| return 0; |
| } |
| |
| } // namespace webrtc |