diff options
author | Haibo Huang <hhb@google.com> | 2021-02-09 16:44:15 -0800 |
---|---|---|
committer | Haibo Huang <hhb@google.com> | 2021-03-01 23:38:54 +0000 |
commit | c0d5f475d0c6c2d4d2d9dd100921191cf0be7e4d (patch) | |
tree | bec6d2aa3965d92073723d9f41bd28813556c9fa /src | |
parent | 97157061e8c3535f4d4633bd59b04af12f35d416 (diff) | |
download | oboe-c0d5f475d0c6c2d4d2d9dd100921191cf0be7e4d.tar.gz |
Upgrade oboe to bab1c7de64aa1395c83be384056e2bad061ea272
Test: make
Merged-In: I0e69766948e6da6cf547eaf36e880ca08a487d3d
Change-Id: I0e69766948e6da6cf547eaf36e880ca08a487d3d
Diffstat (limited to 'src')
-rw-r--r-- | src/aaudio/AAudioExtensions.h | 172 | ||||
-rw-r--r-- | src/aaudio/AAudioLoader.cpp | 23 | ||||
-rw-r--r-- | src/aaudio/AAudioLoader.h | 11 | ||||
-rw-r--r-- | src/aaudio/AudioStreamAAudio.cpp | 3 | ||||
-rw-r--r-- | src/common/AudioStreamBuilder.cpp | 18 | ||||
-rw-r--r-- | src/common/FilterAudioStream.h | 5 | ||||
-rw-r--r-- | src/common/QuirksManager.cpp | 25 | ||||
-rw-r--r-- | src/common/QuirksManager.h | 10 | ||||
-rw-r--r-- | src/flowgraph/resampler/README.md | 12 |
9 files changed, 258 insertions, 21 deletions
diff --git a/src/aaudio/AAudioExtensions.h b/src/aaudio/AAudioExtensions.h new file mode 100644 index 00000000..51a37ae0 --- /dev/null +++ b/src/aaudio/AAudioExtensions.h @@ -0,0 +1,172 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_AAUDIO_EXTENSIONS_H +#define OBOE_AAUDIO_EXTENSIONS_H + +#include <dlfcn.h> +#include <stdint.h> + +#include <sys/system_properties.h> + +#include "common/OboeDebug.h" +#include "oboe/Oboe.h" +#include "AAudioLoader.h" + +namespace oboe { + +#define LIB_AAUDIO_NAME "libaaudio.so" +#define FUNCTION_IS_MMAP "AAudioStream_isMMapUsed" +#define FUNCTION_SET_MMAP_POLICY "AAudio_setMMapPolicy" +#define FUNCTION_GET_MMAP_POLICY "AAudio_getMMapPolicy" + +#define AAUDIO_ERROR_UNAVAILABLE static_cast<aaudio_result_t>(Result::ErrorUnavailable) + +typedef struct AAudioStreamStruct AAudioStream; + +/** + * Call some AAudio test routines that are not part of the normal API. + */ +class AAudioExtensions { +public: + AAudioExtensions() { + int32_t policy = getIntegerProperty("aaudio.mmap_policy", 0); + mMMapSupported = isPolicyEnabled(policy); + + policy = getIntegerProperty("aaudio.mmap_exclusive_policy", 0); + mMMapExclusiveSupported = isPolicyEnabled(policy); + } + + static bool isPolicyEnabled(int32_t policy) { + return (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS); + } + + static AAudioExtensions &getInstance() { + static AAudioExtensions instance; + return instance; + } + + bool isMMapUsed(oboe::AudioStream *oboeStream) { + AAudioStream *aaudioStream = (AAudioStream *) oboeStream->getUnderlyingStream(); + return isMMapUsed(aaudioStream); + } + + bool isMMapUsed(AAudioStream *aaudioStream) { + if (loadSymbols()) return false; + if (mAAudioStream_isMMap == nullptr) return false; + return mAAudioStream_isMMap(aaudioStream); + } + + /** + * Controls whether the MMAP data path can be selected when opening a stream. + * It has no effect after the stream has been opened. + * It only affects the application that calls it. Other apps are not affected. + * + * @param enabled + * @return 0 or a negative error code + */ + int32_t setMMapEnabled(bool enabled) { + if (loadSymbols()) return AAUDIO_ERROR_UNAVAILABLE; + if (mAAudio_setMMapPolicy == nullptr) return false; + return mAAudio_setMMapPolicy(enabled ? AAUDIO_POLICY_AUTO : AAUDIO_POLICY_NEVER); + } + + bool isMMapEnabled() { + if (loadSymbols()) return false; + if (mAAudio_getMMapPolicy == nullptr) return false; + int32_t policy = mAAudio_getMMapPolicy(); + return isPolicyEnabled(policy); + } + + bool isMMapSupported() { + return mMMapSupported; + } + + bool isMMapExclusiveSupported() { + return mMMapExclusiveSupported; + } + +private: + + enum { + AAUDIO_POLICY_NEVER = 1, + AAUDIO_POLICY_AUTO, + AAUDIO_POLICY_ALWAYS + }; + typedef int32_t aaudio_policy_t; + + int getIntegerProperty(const char *name, int defaultValue) { + int result = defaultValue; + char valueText[PROP_VALUE_MAX] = {0}; + if (__system_property_get(name, valueText) != 0) { + result = atoi(valueText); + } + return result; + } + + /** + * Load the function pointers. + * This can be called multiple times. + * It should only be called from one thread. + * + * @return 0 if successful or negative error. + */ + aaudio_result_t loadSymbols() { + if (mAAudio_getMMapPolicy != nullptr) { + return 0; + } + + void *libHandle = AAudioLoader::getInstance()->getLibHandle(); + if (libHandle == nullptr) { + LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + mAAudioStream_isMMap = (bool (*)(AAudioStream *stream)) + dlsym(libHandle, FUNCTION_IS_MMAP); + if (mAAudioStream_isMMap == nullptr) { + LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy)) + dlsym(libHandle, FUNCTION_SET_MMAP_POLICY); + if (mAAudio_setMMapPolicy == nullptr) { + LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + mAAudio_getMMapPolicy = (aaudio_policy_t (*)()) + dlsym(libHandle, FUNCTION_GET_MMAP_POLICY); + if (mAAudio_getMMapPolicy == nullptr) { + LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__); + return AAUDIO_ERROR_UNAVAILABLE; + } + + return 0; + } + + bool mMMapSupported = false; + bool mMMapExclusiveSupported = false; + + bool (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr; + int32_t (*mAAudio_setMMapPolicy)(aaudio_policy_t policy) = nullptr; + aaudio_policy_t (*mAAudio_getMMapPolicy)() = nullptr; +}; + +} // namespace oboe + +#endif //OBOE_AAUDIO_EXTENSIONS_H diff --git a/src/aaudio/AAudioLoader.cpp b/src/aaudio/AAudioLoader.cpp index 064633c6..ec97a26e 100644 --- a/src/aaudio/AAudioLoader.cpp +++ b/src/aaudio/AAudioLoader.cpp @@ -24,10 +24,17 @@ namespace oboe { AAudioLoader::~AAudioLoader() { - if (mLibHandle != nullptr) { - dlclose(mLibHandle); - mLibHandle = nullptr; - } + // Issue 360: thread_local variables with non-trivial destructors + // will cause segfaults if the containing library is dlclose()ed on + // devices running M or newer, or devices before M when using a static STL. + // The simple workaround is to not call dlclose. + // https://github.com/android/ndk/wiki/Changelog-r22#known-issues + // + // The libaaudio and libaaudioclient do not use thread_local. + // But, to be safe, we should avoid dlclose() if possible. + // Because AAudioLoader is a static Singleton, we can safely skip + // calling dlclose() without causing a resource leak. + LOGI("%s() dlclose(%s) not called, OK", __func__, LIB_AAUDIO_NAME); } AAudioLoader* AAudioLoader::getInstance() { @@ -90,8 +97,6 @@ int AAudioLoader::open() { stream_getTimestamp = load_I_PSKPLPL("AAudioStream_getTimestamp"); - stream_isMMapUsed = load_B_PS("AAudioStream_isMMapUsed"); - stream_getChannelCount = load_I_PS("AAudioStream_getChannelCount"); if (stream_getChannelCount == nullptr) { // Use old alias if needed. @@ -304,7 +309,6 @@ AAudioLoader::signature_I_PSKPLPL AAudioLoader::load_I_PSKPLPL(const char *funct == AAUDIO_PERFORMANCE_MODE_POWER_SAVING, ERRMSG); static_assert((int32_t)PerformanceMode::LowLatency == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, ERRMSG); -#endif // The aaudio_ usage, content and input_preset types were added in NDK 17, // which is the first version to support Android Pie (API 28). @@ -343,6 +347,9 @@ AAudioLoader::signature_I_PSKPLPL AAudioLoader::load_I_PSKPLPL(const char *funct static_assert((int32_t)SessionId::None == AAUDIO_SESSION_ID_NONE, ERRMSG); static_assert((int32_t)SessionId::Allocate == AAUDIO_SESSION_ID_ALLOCATE, ERRMSG); -#endif + +#endif // __NDK_MAJOR__ >= 17 + +#endif // AAUDIO_AAUDIO_H } // namespace oboe diff --git a/src/aaudio/AAudioLoader.h b/src/aaudio/AAudioLoader.h index 6f5bb950..a07c5391 100644 --- a/src/aaudio/AAudioLoader.h +++ b/src/aaudio/AAudioLoader.h @@ -52,6 +52,12 @@ typedef int32_t aaudio_usage_t; typedef int32_t aaudio_content_type_t; typedef int32_t aaudio_input_preset_t; typedef int32_t aaudio_session_id_t; + +// There are a few definitions used by Oboe. +#define AAUDIO_OK static_cast<aaudio_result_t>(Result::OK) +#define AAUDIO_ERROR_TIMEOUT static_cast<aaudio_result_t>(Result::ErrorTimeout) +#define AAUDIO_STREAM_STATE_STARTING static_cast<aaudio_stream_state_t>(StreamState::Starting) +#define AAUDIO_STREAM_STATE_STARTED static_cast<aaudio_stream_state_t>(StreamState::Started) #else #include <aaudio/AAudio.h> #include <android/ndk-version.h> @@ -63,7 +69,6 @@ typedef int32_t aaudio_session_id_t; namespace oboe { - /** * The AAudio API was not available in early versions of Android. * To avoid linker errors, we dynamically link with the functions by name using dlsym(). @@ -133,6 +138,8 @@ class AAudioLoader { */ int open(); + void *getLibHandle() const { return mLibHandle; } + // Function pointers into the AAudio shared library. signature_I_PPB createStreamBuilder = nullptr; @@ -167,8 +174,6 @@ class AAudioLoader { signature_I_PSKPLPL stream_getTimestamp = nullptr; - signature_B_PS stream_isMMapUsed = nullptr; - signature_I_PS stream_close = nullptr; signature_I_PS stream_getChannelCount = nullptr; diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp index cb817623..bc535aeb 100644 --- a/src/aaudio/AudioStreamAAudio.cpp +++ b/src/aaudio/AudioStreamAAudio.cpp @@ -23,6 +23,7 @@ #include "common/AudioClock.h" #include "common/OboeDebug.h" #include "oboe/Utilities.h" +#include "AAudioExtensions.h" #ifdef __ANDROID__ #include <sys/system_properties.h> @@ -677,7 +678,7 @@ ResultWithValue<double> AudioStreamAAudio::calculateLatencyMillis() { bool AudioStreamAAudio::isMMapUsed() { AAudioStream *stream = mAAudioStream.load(); if (stream != nullptr) { - return mLibLoader->stream_isMMapUsed(stream); + return AAudioExtensions::getInstance().isMMapUsed(stream); } else { return false; } diff --git a/src/common/AudioStreamBuilder.cpp b/src/common/AudioStreamBuilder.cpp index dffcd75e..b9f04b85 100644 --- a/src/common/AudioStreamBuilder.cpp +++ b/src/common/AudioStreamBuilder.cpp @@ -16,6 +16,8 @@ #include <sys/types.h> + +#include "aaudio/AAudioExtensions.h" #include "aaudio/AudioStreamAAudio.h" #include "FilterAudioStream.h" #include "OboeDebug.h" @@ -109,7 +111,6 @@ Result AudioStreamBuilder::openStream(AudioStream **streamPP) { // Do we need to make a child stream and convert. if (conversionNeeded) { AudioStream *tempStream; - result = childBuilder.openStream(&tempStream); if (result != Result::OK) { return result; @@ -156,7 +157,20 @@ Result AudioStreamBuilder::openStream(AudioStream **streamPP) { } } - result = streamP->open(); // TODO review API + // If MMAP has a problem in this case then disable it temporarily. + bool wasMMapOriginallyEnabled = AAudioExtensions::getInstance().isMMapEnabled(); + bool wasMMapTemporarilyDisabled = false; + if (wasMMapOriginallyEnabled) { + bool isMMapSafe = QuirksManager::getInstance().isMMapSafe(childBuilder); + if (!isMMapSafe) { + AAudioExtensions::getInstance().setMMapEnabled(false); + wasMMapTemporarilyDisabled = true; + } + } + result = streamP->open(); + if (wasMMapTemporarilyDisabled) { + AAudioExtensions::getInstance().setMMapEnabled(wasMMapOriginallyEnabled); // restore original + } if (result == Result::OK) { int32_t optimalBufferSize = -1; diff --git a/src/common/FilterAudioStream.h b/src/common/FilterAudioStream.h index 210e9d11..5428db5b 100644 --- a/src/common/FilterAudioStream.h +++ b/src/common/FilterAudioStream.h @@ -173,7 +173,10 @@ public: int64_t *timeNanoseconds) override { int64_t childPosition = 0; Result result = mChildStream->getTimestamp(clockId, &childPosition, timeNanoseconds); - *framePosition = childPosition * mRateScaler; + // It is OK if framePosition is null. + if (framePosition) { + *framePosition = childPosition * mRateScaler; + } return result; } diff --git a/src/common/QuirksManager.cpp b/src/common/QuirksManager.cpp index aa285de0..5d35c71b 100644 --- a/src/common/QuirksManager.cpp +++ b/src/common/QuirksManager.cpp @@ -20,10 +20,6 @@ #include "OboeDebug.h" #include "QuirksManager.h" -#ifndef __ANDROID_API_R__ -#define __ANDROID_API_R__ 30 -#endif - using namespace oboe; int32_t QuirksManager::DeviceQuirks::clipBufferSize(AudioStream &stream, @@ -74,6 +70,9 @@ public: std::string chipname = getPropertyString("ro.hardware.chipname"); isExynos9810 = (chipname == "exynos9810"); + isExynos990 = (chipname == "exynos990"); + + mBuildChangelist = getPropertyInteger("ro.build.changelist", 0); } virtual ~SamsungDeviceQuirks() = default; @@ -98,6 +97,17 @@ public: && builder.getInputPreset() != oboe::InputPreset::Camcorder; } + bool isMMapSafe(const AudioStreamBuilder &builder) override { + const bool isInput = builder.getDirection() == Direction::Input; + // This detects b/159066712 , S20 LSI has corrupt low latency audio recording + // and turns off MMAP. + // See also https://github.com/google/oboe/issues/892 + bool mRecordingCorrupted = isInput + && isExynos990 + && mBuildChangelist < 19350896; + return !mRecordingCorrupted; + } + private: // Stay farther away from DSP position on Exynos devices. static constexpr int32_t kBottomMarginExynos = 2; @@ -105,6 +115,8 @@ private: static constexpr int32_t kTopMargin = 1; bool isExynos = false; bool isExynos9810 = false; + bool isExynos990 = false; + int mBuildChangelist = 0; }; QuirksManager::QuirksManager() { @@ -203,3 +215,8 @@ bool QuirksManager::isConversionNeeded( return conversionNeeded; } + +bool QuirksManager::isMMapSafe(AudioStreamBuilder &builder) { + if (!OboeGlobals::areWorkaroundsEnabled()) return true; + return mDeviceQuirks->isMMapSafe(builder); +} diff --git a/src/common/QuirksManager.h b/src/common/QuirksManager.h index b4e38ded..aeca270a 100644 --- a/src/common/QuirksManager.h +++ b/src/common/QuirksManager.h @@ -21,6 +21,10 @@ #include <oboe/AudioStreamBuilder.h> #include <aaudio/AudioStreamAAudio.h> +#ifndef __ANDROID_API_R__ +#define __ANDROID_API_R__ 30 +#endif + namespace oboe { /** @@ -98,6 +102,10 @@ public: virtual bool isAAudioMMapPossible(const AudioStreamBuilder &builder) const; + virtual bool isMMapSafe(const AudioStreamBuilder & /* builder */ ) { + return true; + } + static constexpr int32_t kDefaultBottomMarginInBursts = 0; static constexpr int32_t kDefaultTopMarginInBursts = 0; @@ -108,6 +116,8 @@ public: static constexpr int32_t kCommonNativeRate = 48000; // very typical native sample rate }; + bool isMMapSafe(AudioStreamBuilder &builder); + private: static constexpr int32_t kChannelCountMono = 1; diff --git a/src/flowgraph/resampler/README.md b/src/flowgraph/resampler/README.md index 5ea97dc8..eaea99f2 100644 --- a/src/flowgraph/resampler/README.md +++ b/src/flowgraph/resampler/README.md @@ -1,12 +1,20 @@ # Sample Rate Converter This folder contains a sample rate converter, or "resampler". -It is part of [Oboe](https://github.com/google/oboe) but has no dependencies on Oboe. -So the contents of this folder can be used outside of Oboe. The converter is based on a sinc function that has been windowed by a hyperbolic cosine. We found this had fewer artifacts than the more traditional Kaiser window. +## Building the Resampler + +It is part of [Oboe](https://github.com/google/oboe) but has no dependencies on Oboe. +So the contents of this folder can be used outside of Oboe. + +To build it for use outside of Oboe: + +1. Copy the "resampler" folder to a folder in your project that is in the include path. +2. Add all of the \*.cpp files in the resampler folder to your project IDE or Makefile. + ## Creating a Resampler Include the [main header](MultiChannelResampler.h) for the resampler. |