Skip to content

Commit 6d5a4f9

Browse files
committed
oboe: use proxy for callback size adapter
Avoid using callback size adapters in AAudio because of various bugs. If setFramesPerCallback() called then use a filter stream and do the block size adaptation in Oboe as part of the data conversion flowgraph. Fixes #778 Fixes #973 Fixes #983
1 parent 0284674 commit 6d5a4f9

3 files changed

Lines changed: 45 additions & 12 deletions

File tree

src/common/AudioStreamBuilder.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,10 @@ AudioStream *AudioStreamBuilder::build() {
8080
}
8181

8282
bool AudioStreamBuilder::isCompatible(AudioStreamBase &other) {
83-
return getSampleRate() == other.getSampleRate()
84-
&& getFormat() == other.getFormat()
85-
&& getChannelCount() == other.getChannelCount();
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());
8687
}
8788

8889
Result AudioStreamBuilder::openStream(AudioStream **streamPP) {
@@ -111,7 +112,7 @@ Result AudioStreamBuilder::openStream(AudioStream **streamPP) {
111112
}
112113

113114
if (isCompatible(*tempStream)) {
114-
// Everything matches so we can just use the child stream directly.
115+
// The child stream would work as the requested stream so we can just use it directly.
115116
*streamPP = tempStream;
116117
return result;
117118
} else {
@@ -126,6 +127,9 @@ Result AudioStreamBuilder::openStream(AudioStream **streamPP) {
126127
if (getSampleRate() == oboe::Unspecified) {
127128
parentBuilder.setSampleRate(tempStream->getSampleRate());
128129
}
130+
if (getFramesPerCallback() == oboe::Unspecified) {
131+
parentBuilder.setFramesPerCallback(tempStream->getFramesPerCallback());
132+
}
129133

130134
// Use childStream in a FilterAudioStream.
131135
LOGI("%s() create a FilterAudioStream for data conversion.", __func__);

src/common/DataConversionFlowGraph.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,34 +82,38 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream
8282
AudioFormat sourceFormat = sourceStream->getFormat();
8383
int32_t sourceChannelCount = sourceStream->getChannelCount();
8484
int32_t sourceSampleRate = sourceStream->getSampleRate();
85+
int32_t sourceFramesPerCallback = sourceStream->getFramesPerCallback();
8586

8687
AudioFormat sinkFormat = sinkStream->getFormat();
8788
int32_t sinkChannelCount = sinkStream->getChannelCount();
8889
int32_t sinkSampleRate = sinkStream->getSampleRate();
90+
int32_t sinkFramesPerCallback = sinkStream->getFramesPerCallback();
8991

90-
LOGI("%s() flowgraph converts channels: %d to %d, format: %d to %d, rate: %d to %d, qual = %d",
92+
LOGI("%s() flowgraph converts channels: %d to %d, format: %d to %d"
93+
", rate: %d to %d, cbsize: %d to %d, qual = %d",
9194
__func__,
9295
sourceChannelCount, sinkChannelCount,
9396
sourceFormat, sinkFormat,
9497
sourceSampleRate, sinkSampleRate,
98+
sourceFramesPerCallback, sinkFramesPerCallback,
9599
sourceStream->getSampleRateConversionQuality());
96100

97-
int32_t framesPerCallback = (sourceStream->getFramesPerCallback() == kUnspecified)
98-
? sourceStream->getFramesPerBurst()
99-
: sourceStream->getFramesPerCallback();
100101
// Source
101102
// IF OUTPUT and using a callback then call back to the app using a SourceCaller.
102103
// OR IF INPUT and NOT using a callback then read from the child stream using a SourceCaller.
103104
if ((sourceStream->getCallback() != nullptr && isOutput)
104105
|| (sourceStream->getCallback() == nullptr && isInput)) {
106+
int32_t actualSourceFramesPerCallback = (sourceFramesPerCallback == kUnspecified)
107+
? sourceStream->getFramesPerBurst()
108+
: sourceFramesPerCallback;
105109
switch (sourceFormat) {
106110
case AudioFormat::Float:
107111
mSourceCaller = std::make_unique<SourceFloatCaller>(sourceChannelCount,
108-
framesPerCallback);
112+
actualSourceFramesPerCallback);
109113
break;
110114
case AudioFormat::I16:
111115
mSourceCaller = std::make_unique<SourceI16Caller>(sourceChannelCount,
112-
framesPerCallback);
116+
actualSourceFramesPerCallback);
113117
break;
114118
default:
115119
LOGE("%s() Unsupported source caller format = %d", __func__, sourceFormat);
@@ -132,8 +136,11 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream
132136
return Result::ErrorIllegalArgument;
133137
}
134138
if (isInput) {
139+
int32_t actualSinkFramesPerCallback = (sinkFramesPerCallback == kUnspecified)
140+
? sinkStream->getFramesPerBurst()
141+
: sinkFramesPerCallback;
135142
// The BlockWriter is after the Sink so use the SinkStream size.
136-
mBlockWriter.open(framesPerCallback * sinkStream->getBytesPerFrame());
143+
mBlockWriter.open(actualSinkFramesPerCallback * sinkStream->getBytesPerFrame());
137144
mAppBuffer = std::make_unique<uint8_t[]>(
138145
kDefaultBufferSize * sinkStream->getBytesPerFrame());
139146
}
@@ -158,7 +165,7 @@ Result DataConversionFlowGraph::configure(AudioStream *sourceStream, AudioStream
158165

159166
// Sample Rate conversion
160167
if (sourceSampleRate != sinkSampleRate) {
161-
// Create a resample to do the math.
168+
// Create a resampler to do the math.
162169
mResampler.reset(MultiChannelResampler::make(lastOutput->getSamplesPerFrame(),
163170
sourceSampleRate,
164171
sinkSampleRate,

src/common/QuirksManager.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,28 @@ bool QuirksManager::isConversionNeeded(
120120
const bool isInput = builder.getDirection() == Direction::Input;
121121
const bool isFloat = builder.getFormat() == AudioFormat::Float;
122122

123+
// There are multiple bugs involving using callback with a specified callback size.
124+
// Issue #778: O to Q had a problem with Legacy INPUT streams for FLOAT streams
125+
// and a specified callback size. It would assert because of a bad buffer size.
126+
//
127+
// Issue #973: O to R had a problem with Legacy output streams using callback and a specified callback size.
128+
// An AudioTrack stream could still be running when the AAudio FixedBlockReader was closed.
129+
// Internally b/161914201#comment25
130+
//
131+
// Issue #983: O to R would glitch if the framesPerCallback was too small.
132+
//
133+
// Most of these problems were related to Legacy stream. MMAP was OK. But we don't
134+
// know if we will get an MMAP stream. So, to be safe, just do the conversion in Oboe.
135+
if (OboeGlobals::areWorkaroundsEnabled()
136+
&& builder.willUseAAudio()
137+
&& builder.getCallback() != nullptr
138+
&& builder.getFramesPerCallback() != 0
139+
&& getSdkVersion() <= __ANDROID_API_R__) {
140+
LOGI("QuirksManager::%s() avoid setFramesPerCallback(n>0)", __func__);
141+
childBuilder.setFramesPerCallback(oboe::Unspecified);
142+
conversionNeeded = true;
143+
}
144+
123145
// If a SAMPLE RATE is specified for low latency then let the native code choose an optimal rate.
124146
// TODO There may be a problem if the devices supports low latency
125147
// at a higher rate than the default.

0 commit comments

Comments
 (0)