/* * Copyright (c) 2015 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 "modules/audio_device/android/audio_manager.h" #include #include "modules/audio_device/android/audio_common.h" #include "modules/utility/include/helpers_android.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/platform_thread.h" namespace webrtc { // AudioManager::JavaAudioManager implementation AudioManager::JavaAudioManager::JavaAudioManager( NativeRegistration* native_reg, std::unique_ptr audio_manager) : audio_manager_(std::move(audio_manager)), init_(native_reg->GetMethodId("init", "()Z")), dispose_(native_reg->GetMethodId("dispose", "()V")), is_communication_mode_enabled_( native_reg->GetMethodId("isCommunicationModeEnabled", "()Z")), is_device_blacklisted_for_open_sles_usage_( native_reg->GetMethodId("isDeviceBlacklistedForOpenSLESUsage", "()Z")) { RTC_LOG(INFO) << "JavaAudioManager::ctor"; } AudioManager::JavaAudioManager::~JavaAudioManager() { RTC_LOG(INFO) << "JavaAudioManager::~dtor"; } bool AudioManager::JavaAudioManager::Init() { return audio_manager_->CallBooleanMethod(init_); } void AudioManager::JavaAudioManager::Close() { audio_manager_->CallVoidMethod(dispose_); } bool AudioManager::JavaAudioManager::IsCommunicationModeEnabled() { return audio_manager_->CallBooleanMethod(is_communication_mode_enabled_); } bool AudioManager::JavaAudioManager::IsDeviceBlacklistedForOpenSLESUsage() { return audio_manager_->CallBooleanMethod( is_device_blacklisted_for_open_sles_usage_); } // AudioManager implementation AudioManager::AudioManager() : j_environment_(JVM::GetInstance()->environment()), audio_layer_(AudioDeviceModule::kPlatformDefaultAudio), initialized_(false), hardware_aec_(false), hardware_agc_(false), hardware_ns_(false), low_latency_playout_(false), low_latency_record_(false), delay_estimate_in_milliseconds_(0) { RTC_LOG(INFO) << "ctor"; RTC_CHECK(j_environment_); JNINativeMethod native_methods[] = { {"nativeCacheAudioParameters", "(IIIZZZZZZZIIJ)V", reinterpret_cast(&webrtc::AudioManager::CacheAudioParameters)}}; j_native_registration_ = j_environment_->RegisterNatives( "org/webrtc/voiceengine/WebRtcAudioManager", native_methods, arraysize(native_methods)); j_audio_manager_.reset( new JavaAudioManager(j_native_registration_.get(), j_native_registration_->NewObject( "", "(J)V", PointerTojlong(this)))); } AudioManager::~AudioManager() { RTC_LOG(INFO) << "dtor"; RTC_DCHECK(thread_checker_.IsCurrent()); Close(); } void AudioManager::SetActiveAudioLayer( AudioDeviceModule::AudioLayer audio_layer) { RTC_LOG(INFO) << "SetActiveAudioLayer: " << audio_layer; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!initialized_); // Store the currently utilized audio layer. audio_layer_ = audio_layer; // The delay estimate can take one of two fixed values depending on if the // device supports low-latency output or not. However, it is also possible // that the user explicitly selects the high-latency audio path, hence we use // the selected |audio_layer| here to set the delay estimate. delay_estimate_in_milliseconds_ = (audio_layer == AudioDeviceModule::kAndroidJavaAudio) ? kHighLatencyModeDelayEstimateInMilliseconds : kLowLatencyModeDelayEstimateInMilliseconds; RTC_LOG(INFO) << "delay_estimate_in_milliseconds: " << delay_estimate_in_milliseconds_; } SLObjectItf AudioManager::GetOpenSLEngine() { RTC_LOG(INFO) << "GetOpenSLEngine"; RTC_DCHECK(thread_checker_.IsCurrent()); // Only allow usage of OpenSL ES if such an audio layer has been specified. if (audio_layer_ != AudioDeviceModule::kAndroidOpenSLESAudio && audio_layer_ != AudioDeviceModule::kAndroidJavaInputAndOpenSLESOutputAudio) { RTC_LOG(INFO) << "Unable to create OpenSL engine for the current audio layer: " << audio_layer_; return nullptr; } // OpenSL ES for Android only supports a single engine per application. // If one already has been created, return existing object instead of // creating a new. if (engine_object_.Get() != nullptr) { RTC_LOG(WARNING) << "The OpenSL ES engine object has already been created"; return engine_object_.Get(); } // Create the engine object in thread safe mode. const SLEngineOption option[] = { {SL_ENGINEOPTION_THREADSAFE, static_cast(SL_BOOLEAN_TRUE)}}; SLresult result = slCreateEngine(engine_object_.Receive(), 1, option, 0, NULL, NULL); if (result != SL_RESULT_SUCCESS) { RTC_LOG(LS_ERROR) << "slCreateEngine() failed: " << GetSLErrorString(result); engine_object_.Reset(); return nullptr; } // Realize the SL Engine in synchronous mode. result = engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE); if (result != SL_RESULT_SUCCESS) { RTC_LOG(LS_ERROR) << "Realize() failed: " << GetSLErrorString(result); engine_object_.Reset(); return nullptr; } // Finally return the SLObjectItf interface of the engine object. return engine_object_.Get(); } bool AudioManager::Init() { RTC_LOG(INFO) << "Init"; RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK(!initialized_); RTC_DCHECK_NE(audio_layer_, AudioDeviceModule::kPlatformDefaultAudio); if (!j_audio_manager_->Init()) { RTC_LOG(LS_ERROR) << "Init() failed"; return false; } initialized_ = true; return true; } bool AudioManager::Close() { RTC_LOG(INFO) << "Close"; RTC_DCHECK(thread_checker_.IsCurrent()); if (!initialized_) return true; j_audio_manager_->Close(); initialized_ = false; return true; } bool AudioManager::IsCommunicationModeEnabled() const { RTC_DCHECK(thread_checker_.IsCurrent()); return j_audio_manager_->IsCommunicationModeEnabled(); } bool AudioManager::IsAcousticEchoCancelerSupported() const { RTC_DCHECK(thread_checker_.IsCurrent()); return hardware_aec_; } bool AudioManager::IsAutomaticGainControlSupported() const { RTC_DCHECK(thread_checker_.IsCurrent()); return hardware_agc_; } bool AudioManager::IsNoiseSuppressorSupported() const { RTC_DCHECK(thread_checker_.IsCurrent()); return hardware_ns_; } bool AudioManager::IsLowLatencyPlayoutSupported() const { RTC_DCHECK(thread_checker_.IsCurrent()); // Some devices are blacklisted for usage of OpenSL ES even if they report // that low-latency playout is supported. See b/21485703 for details. return j_audio_manager_->IsDeviceBlacklistedForOpenSLESUsage() ? false : low_latency_playout_; } bool AudioManager::IsLowLatencyRecordSupported() const { RTC_DCHECK(thread_checker_.IsCurrent()); return low_latency_record_; } bool AudioManager::IsProAudioSupported() const { RTC_DCHECK(thread_checker_.IsCurrent()); // TODO(henrika): return the state independently of if OpenSL ES is // blacklisted or not for now. We could use the same approach as in // IsLowLatencyPlayoutSupported() but I can't see the need for it yet. return pro_audio_; } // TODO(henrika): improve comments... bool AudioManager::IsAAudioSupported() const { #if defined(WEBRTC_AUDIO_DEVICE_INCLUDE_ANDROID_AAUDIO) return a_audio_; #else return false; #endif } bool AudioManager::IsStereoPlayoutSupported() const { RTC_DCHECK(thread_checker_.IsCurrent()); return (playout_parameters_.channels() == 2); } bool AudioManager::IsStereoRecordSupported() const { RTC_DCHECK(thread_checker_.IsCurrent()); return (record_parameters_.channels() == 2); } int AudioManager::GetDelayEstimateInMilliseconds() const { return delay_estimate_in_milliseconds_; } JNI_FUNCTION_ALIGN void JNICALL AudioManager::CacheAudioParameters(JNIEnv* env, jobject obj, jint sample_rate, jint output_channels, jint input_channels, jboolean hardware_aec, jboolean hardware_agc, jboolean hardware_ns, jboolean low_latency_output, jboolean low_latency_input, jboolean pro_audio, jboolean a_audio, jint output_buffer_size, jint input_buffer_size, jlong native_audio_manager) { webrtc::AudioManager* this_object = reinterpret_cast(native_audio_manager); this_object->OnCacheAudioParameters( env, sample_rate, output_channels, input_channels, hardware_aec, hardware_agc, hardware_ns, low_latency_output, low_latency_input, pro_audio, a_audio, output_buffer_size, input_buffer_size); } void AudioManager::OnCacheAudioParameters(JNIEnv* env, jint sample_rate, jint output_channels, jint input_channels, jboolean hardware_aec, jboolean hardware_agc, jboolean hardware_ns, jboolean low_latency_output, jboolean low_latency_input, jboolean pro_audio, jboolean a_audio, jint output_buffer_size, jint input_buffer_size) { RTC_LOG(INFO) << "OnCacheAudioParameters: " "hardware_aec: " << static_cast(hardware_aec) << ", hardware_agc: " << static_cast(hardware_agc) << ", hardware_ns: " << static_cast(hardware_ns) << ", low_latency_output: " << static_cast(low_latency_output) << ", low_latency_input: " << static_cast(low_latency_input) << ", pro_audio: " << static_cast(pro_audio) << ", a_audio: " << static_cast(a_audio) << ", sample_rate: " << static_cast(sample_rate) << ", output_channels: " << static_cast(output_channels) << ", input_channels: " << static_cast(input_channels) << ", output_buffer_size: " << static_cast(output_buffer_size) << ", input_buffer_size: " << static_cast(input_buffer_size); RTC_DCHECK(thread_checker_.IsCurrent()); hardware_aec_ = hardware_aec; hardware_agc_ = hardware_agc; hardware_ns_ = hardware_ns; low_latency_playout_ = low_latency_output; low_latency_record_ = low_latency_input; pro_audio_ = pro_audio; a_audio_ = a_audio; playout_parameters_.reset(sample_rate, static_cast(output_channels), static_cast(output_buffer_size)); record_parameters_.reset(sample_rate, static_cast(input_channels), static_cast(input_buffer_size)); } const AudioParameters& AudioManager::GetPlayoutAudioParameters() { RTC_CHECK(playout_parameters_.is_valid()); RTC_DCHECK(thread_checker_.IsCurrent()); return playout_parameters_; } const AudioParameters& AudioManager::GetRecordAudioParameters() { RTC_CHECK(record_parameters_.is_valid()); RTC_DCHECK(thread_checker_.IsCurrent()); return record_parameters_; } } // namespace webrtc