1 /*
2 * Copyright 2016 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 <sys/types.h>
18
19 #include "aaudio/AudioStreamAAudio.h"
20 #include "FilterAudioStream.h"
21 #include "OboeDebug.h"
22 #include "oboe/Oboe.h"
23 #include "oboe/AudioStreamBuilder.h"
24 #include "opensles/AudioInputStreamOpenSLES.h"
25 #include "opensles/AudioOutputStreamOpenSLES.h"
26 #include "opensles/AudioStreamOpenSLES.h"
27 #include "QuirksManager.h"
28
29 bool oboe::OboeGlobals::mWorkaroundsEnabled = true;
30
31 namespace oboe {
32
33 /**
34 * The following default values are used when oboe does not have any better way of determining the optimal values
35 * for an audio stream. This can happen when:
36 *
37 * - Client is creating a stream on API < 26 (OpenSLES) but has not supplied the optimal sample
38 * rate and/or frames per burst
39 * - Client is creating a stream on API 16 (OpenSLES) where AudioManager.PROPERTY_OUTPUT_* values
40 * are not available
41 */
42 int32_t DefaultStreamValues::SampleRate = 48000; // Common rate for mobile audio and video
43 int32_t DefaultStreamValues::FramesPerBurst = 192; // 4 msec at 48000 Hz
44 int32_t DefaultStreamValues::ChannelCount = 2; // Stereo
45
46 constexpr int kBufferSizeInBurstsForLowLatencyStreams = 2;
47
48 #ifndef OBOE_ENABLE_AAUDIO
49 // Set OBOE_ENABLE_AAUDIO to 0 if you want to disable the AAudio API.
50 // This might be useful if you want to force all the unit tests to use OpenSL ES.
51 #define OBOE_ENABLE_AAUDIO 1
52 #endif
53
isAAudioSupported()54 bool AudioStreamBuilder::isAAudioSupported() {
55 return AudioStreamAAudio::isSupported() && OBOE_ENABLE_AAUDIO;
56 }
57
isAAudioRecommended()58 bool AudioStreamBuilder::isAAudioRecommended() {
59 // See https://github.com/google/oboe/issues/40,
60 // AAudio may not be stable on Android O, depending on how it is used.
61 // To be safe, use AAudio only on O_MR1 and above.
62 return (getSdkVersion() >= __ANDROID_API_O_MR1__) && isAAudioSupported();
63 }
64
build()65 AudioStream *AudioStreamBuilder::build() {
66 AudioStream *stream = nullptr;
67 if (isAAudioRecommended() && mAudioApi != AudioApi::OpenSLES) {
68 stream = new AudioStreamAAudio(*this);
69 } else if (isAAudioSupported() && mAudioApi == AudioApi::AAudio) {
70 stream = new AudioStreamAAudio(*this);
71 LOGE("Creating AAudio stream on 8.0 because it was specified. This is error prone.");
72 } else {
73 if (getDirection() == oboe::Direction::Output) {
74 stream = new AudioOutputStreamOpenSLES(*this);
75 } else if (getDirection() == oboe::Direction::Input) {
76 stream = new AudioInputStreamOpenSLES(*this);
77 }
78 }
79 return stream;
80 }
81
isCompatible(AudioStreamBase & other)82 bool AudioStreamBuilder::isCompatible(AudioStreamBase &other) {
83 return (getSampleRate() == oboe::Unspecified || getSampleRate() == other.getSampleRate())
84 && (getFormat() == (AudioFormat)oboe::Unspecified || getFormat() == other.getFormat())
85 && (getFramesPerCallback() == oboe::Unspecified || getFramesPerCallback() == other.getFramesPerCallback())
86 && (getChannelCount() == oboe::Unspecified || getChannelCount() == other.getChannelCount());
87 }
88
openStream(AudioStream ** streamPP)89 Result AudioStreamBuilder::openStream(AudioStream **streamPP) {
90 auto result = isValidConfig();
91 if (result != Result::OK) {
92 return result;
93 }
94
95 LOGI("%s() %s -------- %s --------",
96 __func__, getDirection() == Direction::Input ? "INPUT" : "OUTPUT", getVersionText());
97
98 if (streamPP == nullptr) {
99 return Result::ErrorNull;
100 }
101 *streamPP = nullptr;
102
103 AudioStream *streamP = nullptr;
104
105 // Maybe make a FilterInputStream.
106 AudioStreamBuilder childBuilder(*this);
107 // Check need for conversion and modify childBuilder for optimal stream.
108 bool conversionNeeded = QuirksManager::getInstance().isConversionNeeded(*this, childBuilder);
109 // Do we need to make a child stream and convert.
110 if (conversionNeeded) {
111 AudioStream *tempStream;
112
113 result = childBuilder.openStream(&tempStream);
114 if (result != Result::OK) {
115 return result;
116 }
117
118 if (isCompatible(*tempStream)) {
119 // The child stream would work as the requested stream so we can just use it directly.
120 *streamPP = tempStream;
121 return result;
122 } else {
123 AudioStreamBuilder parentBuilder = *this;
124 // Build a stream that is as close as possible to the childStream.
125 if (getFormat() == oboe::AudioFormat::Unspecified) {
126 parentBuilder.setFormat(tempStream->getFormat());
127 }
128 if (getChannelCount() == oboe::Unspecified) {
129 parentBuilder.setChannelCount(tempStream->getChannelCount());
130 }
131 if (getSampleRate() == oboe::Unspecified) {
132 parentBuilder.setSampleRate(tempStream->getSampleRate());
133 }
134 if (getFramesPerCallback() == oboe::Unspecified) {
135 parentBuilder.setFramesPerCallback(tempStream->getFramesPerCallback());
136 }
137
138 // Use childStream in a FilterAudioStream.
139 LOGI("%s() create a FilterAudioStream for data conversion.", __func__);
140 FilterAudioStream *filterStream = new FilterAudioStream(parentBuilder, tempStream);
141 result = filterStream->configureFlowGraph();
142 if (result != Result::OK) {
143 filterStream->close();
144 delete filterStream;
145 // Just open streamP the old way.
146 } else {
147 streamP = static_cast<AudioStream *>(filterStream);
148 }
149 }
150 }
151
152 if (streamP == nullptr) {
153 streamP = build();
154 if (streamP == nullptr) {
155 return Result::ErrorNull;
156 }
157 }
158
159 result = streamP->open(); // TODO review API
160 if (result == Result::OK) {
161
162 int32_t optimalBufferSize = -1;
163 // Use a reasonable default buffer size.
164 if (streamP->getDirection() == Direction::Input) {
165 // For input, small size does not improve latency because the stream is usually
166 // run close to empty. And a low size can result in XRuns so always use the maximum.
167 optimalBufferSize = streamP->getBufferCapacityInFrames();
168 } else if (streamP->getPerformanceMode() == PerformanceMode::LowLatency
169 && streamP->getDirection() == Direction::Output) { // Output check is redundant.
170 optimalBufferSize = streamP->getFramesPerBurst() *
171 kBufferSizeInBurstsForLowLatencyStreams;
172 }
173 if (optimalBufferSize >= 0) {
174 auto setBufferResult = streamP->setBufferSizeInFrames(optimalBufferSize);
175 if (!setBufferResult) {
176 LOGW("Failed to setBufferSizeInFrames(%d). Error was %s",
177 optimalBufferSize,
178 convertToText(setBufferResult.error()));
179 }
180 }
181
182 *streamPP = streamP;
183 } else {
184 delete streamP;
185 }
186 return result;
187 }
188
openManagedStream(oboe::ManagedStream & stream)189 Result AudioStreamBuilder::openManagedStream(oboe::ManagedStream &stream) {
190 stream.reset();
191 auto result = isValidConfig();
192 if (result != Result::OK) {
193 return result;
194 }
195 AudioStream *streamptr;
196 result = openStream(&streamptr);
197 stream.reset(streamptr);
198 return result;
199 }
200
openStream(std::shared_ptr<AudioStream> & sharedStream)201 Result AudioStreamBuilder::openStream(std::shared_ptr<AudioStream> &sharedStream) {
202 sharedStream.reset();
203 auto result = isValidConfig();
204 if (result != Result::OK) {
205 return result;
206 }
207 AudioStream *streamptr;
208 result = openStream(&streamptr);
209 if (result == Result::OK) {
210 sharedStream.reset(streamptr);
211 // Save a weak_ptr in the stream for use with callbacks.
212 streamptr->setWeakThis(sharedStream);
213 }
214 return result;
215 }
216
217 } // namespace oboe
218