From faeb8b20e8ce98d308ca5ec095eeba6583cd9ec6 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Tue, 25 Jul 2017 15:15:07 -0700 Subject: [PATCH] aaudio: fix getXRunCount Mixer was not advancing FIFO when underflowed so the client could not detect the xrun. Also underruns could cause the stream to drift in time relative to other streams. Bug: 64033855 Test: trigger underruns by sleeping in a callback Change-Id: Ia038f5048ffbff3b82e9d86e6807483dfe6879d0 --- .../libaaudio/examples/utils/AAudioSimplePlayer.h | 22 ++++++++++++++++++++-- .../write_sine/src/write_sine_callback.cpp | 12 +++++++----- services/oboeservice/AAudioMixer.cpp | 10 +++++----- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h index 4557f0234b..cc0cb34195 100644 --- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h +++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h @@ -30,6 +30,11 @@ #define SHARING_MODE AAUDIO_SHARING_MODE_SHARED #define PERFORMANCE_MODE AAUDIO_PERFORMANCE_MODE_NONE +// Arbitrary period for glitches, once per second at 48000 Hz. +#define FORCED_UNDERRUN_PERIOD_FRAMES 48000 +// How long to sleep in a callback to cause an intentional glitch. For testing. +#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000) + /** * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode. */ @@ -219,10 +224,13 @@ private: typedef struct SineThreadedData_s { SineGenerator sineOsc1; SineGenerator sineOsc2; + int64_t framesTotal = 0; + int64_t nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES; int32_t minNumFrames = INT32_MAX; int32_t maxNumFrames = 0; int scheduler; - bool schedulerChecked; + bool schedulerChecked = false; + bool forceUnderruns = false; } SineThreadedData_t; // Callback function that fills the audio output buffer. @@ -235,11 +243,21 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( // should not happen but just in case... if (userData == nullptr) { - fprintf(stderr, "ERROR - SimplePlayerDataCallbackProc needs userData\n"); + printf("ERROR - SimplePlayerDataCallbackProc needs userData\n"); return AAUDIO_CALLBACK_RESULT_STOP; } SineThreadedData_t *sineData = (SineThreadedData_t *) userData; + sineData->framesTotal += numFrames; + + if (sineData->forceUnderruns) { + if (sineData->framesTotal > sineData->nextFrameToGlitch) { + usleep(FORCED_UNDERRUN_SLEEP_MICROS); + printf("Simulate glitch at %lld\n", (long long) sineData->framesTotal); + sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES; + } + } + if (!sineData->schedulerChecked) { sineData->scheduler = sched_getscheduler(gettid()); sineData->schedulerChecked = true; diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp index 01cf7e7aae..b5602e97de 100644 --- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp +++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp @@ -26,9 +26,7 @@ #include #include "AAudioExampleUtils.h" #include "AAudioSimplePlayer.h" - -// Application data that gets passed to the callback. -#define MAX_FRAME_COUNT_RECORDS 256 +#include "../../utils/AAudioSimplePlayer.h" int main(int argc, const char **argv) { @@ -42,9 +40,10 @@ int main(int argc, const char **argv) // in a buffer if we hang or crash. setvbuf(stdout, nullptr, _IONBF, (size_t) 0); - printf("%s - Play a sine sweep using an AAudio callback V0.1.1\n", argv[0]); + printf("%s - Play a sine sweep using an AAudio callback V0.1.2\n", argv[0]); myData.schedulerChecked = false; + myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount() if (argParser.parseArgs(argc, argv)) { return EXIT_FAILURE; @@ -99,7 +98,10 @@ int main(int argc, const char **argv) printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state)); break; } - printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(player.getStream())); + printf("framesWritten = %d, underruns = %d\n", + (int) AAudioStream_getFramesWritten(player.getStream()), + (int) AAudioStream_getXRunCount(player.getStream()) + ); } printf("Woke up now.\n"); diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp index 8222734422..952aa82587 100644 --- a/services/oboeservice/AAudioMixer.cpp +++ b/services/oboeservice/AAudioMixer.cpp @@ -88,11 +88,11 @@ bool AAudioMixer::mix(int trackIndex, FifoBuffer *fifo, float volume) { } partIndex++; } - fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst - framesLeft); - if (framesLeft > 0) { - //ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!", - // framesLeft, mFramesPerBurst); - } + // Always advance by one burst even if we do not have the data. + // Otherwise the stream timing will drift whenever there is an underflow. + // This actual underflow can then be detected by the client for XRun counting. + fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst); + #if AAUDIO_MIXER_ATRACE_ENABLED ATRACE_END(); #endif /* AAUDIO_MIXER_ATRACE_ENABLED */ -- 2.11.0