• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <oboe/AudioStreamBuilder.h>
18 #include <oboe/Oboe.h>
19 
20 #include "OboeDebug.h"
21 #include "QuirksManager.h"
22 
23 using namespace oboe;
24 
clipBufferSize(AudioStream & stream,int32_t requestedSize)25 int32_t QuirksManager::DeviceQuirks::clipBufferSize(AudioStream &stream,
26             int32_t requestedSize) {
27     if (!OboeGlobals::areWorkaroundsEnabled()) {
28         return requestedSize;
29     }
30     int bottomMargin = kDefaultBottomMarginInBursts;
31     int topMargin = kDefaultTopMarginInBursts;
32     if (isMMapUsed(stream)) {
33         if (stream.getSharingMode() == SharingMode::Exclusive) {
34             bottomMargin = getExclusiveBottomMarginInBursts();
35             topMargin = getExclusiveTopMarginInBursts();
36         }
37     } else {
38         bottomMargin = kLegacyBottomMarginInBursts;
39     }
40 
41     int32_t burst = stream.getFramesPerBurst();
42     int32_t minSize = bottomMargin * burst;
43     int32_t adjustedSize = requestedSize;
44     if (adjustedSize < minSize ) {
45         adjustedSize = minSize;
46     } else {
47         int32_t maxSize = stream.getBufferCapacityInFrames() - (topMargin * burst);
48         if (adjustedSize > maxSize ) {
49             adjustedSize = maxSize;
50         }
51     }
52     return adjustedSize;
53 }
54 
isAAudioMMapPossible(const AudioStreamBuilder & builder) const55 bool QuirksManager::DeviceQuirks::isAAudioMMapPossible(const AudioStreamBuilder &builder) const {
56     bool isSampleRateCompatible =
57             builder.getSampleRate() == oboe::Unspecified
58             || builder.getSampleRate() == kCommonNativeRate
59             || builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None;
60     return builder.getPerformanceMode() == PerformanceMode::LowLatency
61             && isSampleRateCompatible
62             && builder.getChannelCount() <= kChannelCountStereo;
63 }
64 
65 class SamsungDeviceQuirks : public  QuirksManager::DeviceQuirks {
66 public:
SamsungDeviceQuirks()67     SamsungDeviceQuirks() {
68         std::string arch = getPropertyString("ro.arch");
69         isExynos = (arch.rfind("exynos", 0) == 0); // starts with?
70 
71         std::string chipname = getPropertyString("ro.hardware.chipname");
72         isExynos9810 = (chipname == "exynos9810");
73         isExynos990 = (chipname == "exynos990");
74 
75         mBuildChangelist = getPropertyInteger("ro.build.changelist", 0);
76     }
77 
78     virtual ~SamsungDeviceQuirks() = default;
79 
getExclusiveBottomMarginInBursts() const80     int32_t getExclusiveBottomMarginInBursts() const override {
81         // TODO Make this conditional on build version when MMAP timing improves.
82         return isExynos ? kBottomMarginExynos : kBottomMarginOther;
83     }
84 
getExclusiveTopMarginInBursts() const85     int32_t getExclusiveTopMarginInBursts() const override {
86         return kTopMargin;
87     }
88 
89     // See Oboe issue #824 for more information.
isMonoMMapActuallyStereo() const90     bool isMonoMMapActuallyStereo() const override {
91         return isExynos9810; // TODO We can make this version specific if it gets fixed.
92     }
93 
isAAudioMMapPossible(const AudioStreamBuilder & builder) const94     bool isAAudioMMapPossible(const AudioStreamBuilder &builder) const override {
95         return DeviceQuirks::isAAudioMMapPossible(builder)
96                 // Samsung says they use Legacy for Camcorder
97                 && builder.getInputPreset() != oboe::InputPreset::Camcorder;
98     }
99 
isMMapSafe(const AudioStreamBuilder & builder)100     bool isMMapSafe(const AudioStreamBuilder &builder) override {
101         const bool isInput = builder.getDirection() == Direction::Input;
102         // This detects b/159066712 , S20 LSI has corrupt low latency audio recording
103         // and turns off MMAP.
104         // See also https://github.com/google/oboe/issues/892
105         bool mRecordingCorrupted = isInput
106             && isExynos990
107             && mBuildChangelist < 19350896;
108         return !mRecordingCorrupted;
109     }
110 
111 private:
112     // Stay farther away from DSP position on Exynos devices.
113     static constexpr int32_t kBottomMarginExynos = 2;
114     static constexpr int32_t kBottomMarginOther = 1;
115     static constexpr int32_t kTopMargin = 1;
116     bool isExynos = false;
117     bool isExynos9810 = false;
118     bool isExynos990 = false;
119     int mBuildChangelist = 0;
120 };
121 
QuirksManager()122 QuirksManager::QuirksManager() {
123     std::string manufacturer = getPropertyString("ro.product.manufacturer");
124     if (manufacturer == "samsung") {
125         mDeviceQuirks = std::make_unique<SamsungDeviceQuirks>();
126     } else {
127         mDeviceQuirks = std::make_unique<DeviceQuirks>();
128     }
129 }
130 
isConversionNeeded(const AudioStreamBuilder & builder,AudioStreamBuilder & childBuilder)131 bool QuirksManager::isConversionNeeded(
132         const AudioStreamBuilder &builder,
133         AudioStreamBuilder &childBuilder) {
134     bool conversionNeeded = false;
135     const bool isLowLatency = builder.getPerformanceMode() == PerformanceMode::LowLatency;
136     const bool isInput = builder.getDirection() == Direction::Input;
137     const bool isFloat = builder.getFormat() == AudioFormat::Float;
138 
139     // There are multiple bugs involving using callback with a specified callback size.
140     // Issue #778: O to Q had a problem with Legacy INPUT streams for FLOAT streams
141     // and a specified callback size. It would assert because of a bad buffer size.
142     //
143     // Issue #973: O to R had a problem with Legacy output streams using callback and a specified callback size.
144     // An AudioTrack stream could still be running when the AAudio FixedBlockReader was closed.
145     // Internally b/161914201#comment25
146     //
147     // Issue #983: O to R would glitch if the framesPerCallback was too small.
148     //
149     // Most of these problems were related to Legacy stream. MMAP was OK. But we don't
150     // know if we will get an MMAP stream. So, to be safe, just do the conversion in Oboe.
151     if (OboeGlobals::areWorkaroundsEnabled()
152             && builder.willUseAAudio()
153             && builder.isDataCallbackSpecified()
154             && builder.getFramesPerDataCallback() != 0
155             && getSdkVersion() <= __ANDROID_API_R__) {
156         LOGI("QuirksManager::%s() avoid setFramesPerCallback(n>0)", __func__);
157         childBuilder.setFramesPerCallback(oboe::Unspecified);
158         conversionNeeded = true;
159     }
160 
161     // If a SAMPLE RATE is specified for low latency then let the native code choose an optimal rate.
162     // TODO There may be a problem if the devices supports low latency
163     //      at a higher rate than the default.
164     if (builder.getSampleRate() != oboe::Unspecified
165             && builder.getSampleRateConversionQuality() != SampleRateConversionQuality::None
166             && isLowLatency
167             ) {
168         childBuilder.setSampleRate(oboe::Unspecified); // native API decides the best sample rate
169         conversionNeeded = true;
170     }
171 
172     // Data Format
173     // OpenSL ES and AAudio before P do not support FAST path for FLOAT capture.
174     if (isFloat
175             && isInput
176             && builder.isFormatConversionAllowed()
177             && isLowLatency
178             && (!builder.willUseAAudio() || (getSdkVersion() < __ANDROID_API_P__))
179             ) {
180         childBuilder.setFormat(AudioFormat::I16); // needed for FAST track
181         conversionNeeded = true;
182         LOGI("QuirksManager::%s() forcing internal format to I16 for low latency", __func__);
183     }
184 
185     // Channel Count conversions
186     if (OboeGlobals::areWorkaroundsEnabled()
187             && builder.isChannelConversionAllowed()
188             && builder.getChannelCount() == kChannelCountStereo
189             && isInput
190             && isLowLatency
191             && (!builder.willUseAAudio() && (getSdkVersion() == __ANDROID_API_O__))
192             ) {
193         // Workaround for heap size regression in O.
194         // b/66967812 AudioRecord does not allow FAST track for stereo capture in O
195         childBuilder.setChannelCount(kChannelCountMono);
196         conversionNeeded = true;
197         LOGI("QuirksManager::%s() using mono internally for low latency on O", __func__);
198     } else if (OboeGlobals::areWorkaroundsEnabled()
199                && builder.getChannelCount() == kChannelCountMono
200                && isInput
201                && mDeviceQuirks->isMonoMMapActuallyStereo()
202                && builder.willUseAAudio()
203                // Note: we might use this workaround on a device that supports
204                // MMAP but will use Legacy for this stream.  But this will only happen
205                // on devices that have the broken mono.
206                && mDeviceQuirks->isAAudioMMapPossible(builder)
207                ) {
208         // Workaround for mono actually running in stereo mode.
209         childBuilder.setChannelCount(kChannelCountStereo); // Use stereo and extract first channel.
210         conversionNeeded = true;
211         LOGI("QuirksManager::%s() using stereo internally to avoid broken mono", __func__);
212     }
213     // Note that MMAP does not support mono in 8.1. But that would only matter on Pixel 1
214     // phones and they have almost all been updated to 9.0.
215 
216     return conversionNeeded;
217 }
218 
isMMapSafe(AudioStreamBuilder & builder)219 bool QuirksManager::isMMapSafe(AudioStreamBuilder &builder) {
220     if (!OboeGlobals::areWorkaroundsEnabled()) return true;
221     return mDeviceQuirks->isMMapSafe(builder);
222 }
223