diff options
| author | swurl <swurl@swurl.xyz> | 2025-05-31 02:33:02 -0400 |
|---|---|---|
| committer | swurl <swurl@swurl.xyz> | 2025-05-31 02:33:02 -0400 |
| commit | 6c655321e6b04b8648802e296bb5ca11d290ae68 (patch) | |
| tree | 5c906511e4a2910ce8b806bb9d763b524c28619e /externals/oboe/src/common/QuirksManager.cpp | |
| parent | c0cceff365f754f8859f4c9a0362ebdba7ea388b (diff) | |
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
Diffstat (limited to 'externals/oboe/src/common/QuirksManager.cpp')
| -rw-r--r-- | externals/oboe/src/common/QuirksManager.cpp | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/externals/oboe/src/common/QuirksManager.cpp b/externals/oboe/src/common/QuirksManager.cpp new file mode 100644 index 0000000000..319c7f2551 --- /dev/null +++ b/externals/oboe/src/common/QuirksManager.cpp @@ -0,0 +1,319 @@ +/* + * 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. + */ + +#include <oboe/AudioStreamBuilder.h> +#include <oboe/Oboe.h> +#include <oboe/Utilities.h> + +#include "OboeDebug.h" +#include "QuirksManager.h" + +using namespace oboe; + +int32_t QuirksManager::DeviceQuirks::clipBufferSize(AudioStream &stream, + int32_t requestedSize) { + if (!OboeGlobals::areWorkaroundsEnabled()) { + return requestedSize; + } + int bottomMargin = kDefaultBottomMarginInBursts; + int topMargin = kDefaultTopMarginInBursts; + if (isMMapUsed(stream)) { + if (stream.getSharingMode() == SharingMode::Exclusive) { + bottomMargin = getExclusiveBottomMarginInBursts(); + topMargin = getExclusiveTopMarginInBursts(); + } + } else { + bottomMargin = kLegacyBottomMarginInBursts; + } + + int32_t burst = stream.getFramesPerBurst(); + int32_t minSize = bottomMargin * burst; + int32_t adjustedSize = requestedSize; + if (adjustedSize < minSize ) { + adjustedSize = minSize; + } else { + int32_t maxSize = stream.getBufferCapacityInFrames() - (topMargin * burst); + if (adjustedSize > maxSize ) { + adjustedSize = maxSize; + } + } + return adjustedSize; +} + +bool QuirksManager::DeviceQuirks::isAAudioMMapPossible(const AudioStreamBuilder &builder) const { + bool isSampleRateCompatible = + builder.getSampleRate() == oboe::Unspecified + || builder.getSampleRate() == kCommonNativeRate + || builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None; + return builder.getPerformanceMode() == PerformanceMode::LowLatency + && isSampleRateCompatible + && builder.getChannelCount() <= kChannelCountStereo; +} + +bool QuirksManager::DeviceQuirks::shouldConvertFloatToI16ForOutputStreams() { + std::string productManufacturer = getPropertyString("ro.product.manufacturer"); + if (getSdkVersion() < __ANDROID_API_L__) { + return true; + } else if ((productManufacturer == "vivo") && (getSdkVersion() < __ANDROID_API_M__)) { + return true; + } + return false; +} + +/** + * This is for Samsung Exynos quirks. Samsung Mobile uses Qualcomm chips so + * the QualcommDeviceQuirks would apply. + */ +class SamsungExynosDeviceQuirks : public QuirksManager::DeviceQuirks { +public: + SamsungExynosDeviceQuirks() { + std::string chipname = getPropertyString("ro.hardware.chipname"); + isExynos9810 = (chipname == "exynos9810"); + isExynos990 = (chipname == "exynos990"); + isExynos850 = (chipname == "exynos850"); + + mBuildChangelist = getPropertyInteger("ro.build.changelist", 0); + } + + virtual ~SamsungExynosDeviceQuirks() = default; + + int32_t getExclusiveBottomMarginInBursts() const override { + return kBottomMargin; + } + + int32_t getExclusiveTopMarginInBursts() const override { + return kTopMargin; + } + + // See Oboe issues #824 and #1247 for more information. + bool isMonoMMapActuallyStereo() const override { + return isExynos9810 || isExynos850; // TODO We can make this version specific if it gets fixed. + } + + bool isAAudioMMapPossible(const AudioStreamBuilder &builder) const override { + return DeviceQuirks::isAAudioMMapPossible(builder) + // Samsung says they use Legacy for Camcorder + && 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 isRecordingCorrupted = isInput + && isExynos990 + && mBuildChangelist < 19350896; + + // Certain S9+ builds record silence when using MMAP and not using the VoiceCommunication + // preset. + // See https://github.com/google/oboe/issues/1110 + bool wouldRecordSilence = isInput + && isExynos9810 + && mBuildChangelist <= 18847185 + && (builder.getInputPreset() != InputPreset::VoiceCommunication); + + if (wouldRecordSilence){ + LOGI("QuirksManager::%s() Requested stream configuration would result in silence on " + "this device. Switching off MMAP.", __func__); + } + + return !isRecordingCorrupted && !wouldRecordSilence; + } + +private: + // Stay farther away from DSP position on Exynos devices. + static constexpr int32_t kBottomMargin = 2; + static constexpr int32_t kTopMargin = 1; + bool isExynos9810 = false; + bool isExynos990 = false; + bool isExynos850 = false; + int mBuildChangelist = 0; +}; + +class QualcommDeviceQuirks : public QuirksManager::DeviceQuirks { +public: + QualcommDeviceQuirks() { + std::string modelName = getPropertyString("ro.soc.model"); + isSM8150 = (modelName == "SDM8150"); + } + + virtual ~QualcommDeviceQuirks() = default; + + int32_t getExclusiveBottomMarginInBursts() const override { + return kBottomMargin; + } + + bool isMMapSafe(const AudioStreamBuilder &builder) override { + // See https://github.com/google/oboe/issues/1121#issuecomment-897957749 + bool isMMapBroken = false; + if (isSM8150 && (getSdkVersion() <= __ANDROID_API_P__)) { + LOGI("QuirksManager::%s() MMAP not actually supported on this chip." + " Switching off MMAP.", __func__); + isMMapBroken = true; + } + + return !isMMapBroken; + } + +private: + bool isSM8150 = false; + static constexpr int32_t kBottomMargin = 1; +}; + +QuirksManager::QuirksManager() { + std::string productManufacturer = getPropertyString("ro.product.manufacturer"); + if (productManufacturer == "samsung") { + std::string arch = getPropertyString("ro.arch"); + bool isExynos = (arch.rfind("exynos", 0) == 0); // starts with? + if (isExynos) { + mDeviceQuirks = std::make_unique<SamsungExynosDeviceQuirks>(); + } + } + if (!mDeviceQuirks) { + std::string socManufacturer = getPropertyString("ro.soc.manufacturer"); + if (socManufacturer == "Qualcomm") { + // This may include Samsung Mobile devices. + mDeviceQuirks = std::make_unique<QualcommDeviceQuirks>(); + } else { + mDeviceQuirks = std::make_unique<DeviceQuirks>(); + } + } +} + +bool QuirksManager::isConversionNeeded( + const AudioStreamBuilder &builder, + AudioStreamBuilder &childBuilder) { + bool conversionNeeded = false; + const bool isLowLatency = builder.getPerformanceMode() == PerformanceMode::LowLatency; + const bool isInput = builder.getDirection() == Direction::Input; + const bool isFloat = builder.getFormat() == AudioFormat::Float; + const bool isIEC61937 = builder.getFormat() == AudioFormat::IEC61937; + const bool isCompressed = isCompressedFormat(builder.getFormat()); + + // There should be no conversion for IEC61937. Sample rates and channel counts must be set explicitly. + if (isIEC61937) { + LOGI("QuirksManager::%s() conversion not needed for IEC61937", __func__); + return false; + } + + if (isCompressed) { + LOGI("QuirksManager::%s() conversion not needed for compressed format %d", + __func__, builder.getFormat()); + return false; + } + + // There are multiple bugs involving using callback with a specified callback size. + // Issue #778: O to Q had a problem with Legacy INPUT streams for FLOAT streams + // and a specified callback size. It would assert because of a bad buffer size. + // + // Issue #973: O to R had a problem with Legacy output streams using callback and a specified callback size. + // An AudioTrack stream could still be running when the AAudio FixedBlockReader was closed. + // Internally b/161914201#comment25 + // + // Issue #983: O to R would glitch if the framesPerCallback was too small. + // + // Most of these problems were related to Legacy stream. MMAP was OK. But we don't + // know if we will get an MMAP stream. So, to be safe, just do the conversion in Oboe. + if (OboeGlobals::areWorkaroundsEnabled() + && builder.willUseAAudio() + && builder.isDataCallbackSpecified() + && builder.getFramesPerDataCallback() != 0 + && getSdkVersion() <= __ANDROID_API_R__) { + LOGI("QuirksManager::%s() avoid setFramesPerCallback(n>0)", __func__); + childBuilder.setFramesPerCallback(oboe::Unspecified); + conversionNeeded = true; + } + + // If a SAMPLE RATE is specified for low latency, let the native code choose an optimal rate. + // This isn't really a workaround. It is an Oboe feature that is convenient to place here. + // TODO There may be a problem if the devices supports low latency + // at a higher rate than the default. + if (builder.getSampleRate() != oboe::Unspecified + && builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None + && isLowLatency + ) { + childBuilder.setSampleRate(oboe::Unspecified); // native API decides the best sample rate + conversionNeeded = true; + } + + // Data Format + // OpenSL ES and AAudio before P do not support FAST path for FLOAT capture. + if (OboeGlobals::areWorkaroundsEnabled() + && isFloat + && isInput + && builder.isFormatConversionAllowed() + && isLowLatency + && (!builder.willUseAAudio() || (getSdkVersion() < __ANDROID_API_P__)) + ) { + childBuilder.setFormat(AudioFormat::I16); // needed for FAST track + conversionNeeded = true; + LOGI("QuirksManager::%s() forcing internal format to I16 for low latency", __func__); + } + + // Add quirk for float output when needed. + if (OboeGlobals::areWorkaroundsEnabled() + && isFloat + && !isInput + && builder.isFormatConversionAllowed() + && mDeviceQuirks->shouldConvertFloatToI16ForOutputStreams() + ) { + childBuilder.setFormat(AudioFormat::I16); + conversionNeeded = true; + LOGI("QuirksManager::%s() float was requested but not supported on pre-L devices " + "and some devices like Vivo devices may have issues on L devices, " + "creating an underlying I16 stream and using format conversion to provide a float " + "stream", __func__); + } + + // Channel Count conversions + if (OboeGlobals::areWorkaroundsEnabled() + && builder.isChannelConversionAllowed() + && builder.getChannelCount() == kChannelCountStereo + && isInput + && isLowLatency + && (!builder.willUseAAudio() && (getSdkVersion() == __ANDROID_API_O__)) + ) { + // Workaround for heap size regression in O. + // b/66967812 AudioRecord does not allow FAST track for stereo capture in O + childBuilder.setChannelCount(kChannelCountMono); + conversionNeeded = true; + LOGI("QuirksManager::%s() using mono internally for low latency on O", __func__); + } else if (OboeGlobals::areWorkaroundsEnabled() + && builder.getChannelCount() == kChannelCountMono + && isInput + && mDeviceQuirks->isMonoMMapActuallyStereo() + && builder.willUseAAudio() + // Note: we might use this workaround on a device that supports + // MMAP but will use Legacy for this stream. But this will only happen + // on devices that have the broken mono. + && mDeviceQuirks->isAAudioMMapPossible(builder) + ) { + // Workaround for mono actually running in stereo mode. + childBuilder.setChannelCount(kChannelCountStereo); // Use stereo and extract first channel. + conversionNeeded = true; + LOGI("QuirksManager::%s() using stereo internally to avoid broken mono", __func__); + } + // Note that MMAP does not support mono in 8.1. But that would only matter on Pixel 1 + // phones and they have almost all been updated to 9.0. + + return conversionNeeded; +} + +bool QuirksManager::isMMapSafe(AudioStreamBuilder &builder) { + if (!OboeGlobals::areWorkaroundsEnabled()) return true; + return mDeviceQuirks->isMMapSafe(builder); +} |
