1[Tech Notes Home](README.md) 2 3# Assert in releaseBuffer() 4 5There is a bug that can sometimes cause an assert in ClientProxy or AudioTrackShared::releaseBuffer() when headsets are connected or disconnected. 6The bug was originally reported at: https://github.com/google/oboe/issues/535 7 8You will see signatures like this in the logcat: 9 10 F AudioTrackShared: releaseBuffer: mUnreleased out of range, !(stepCount:96 <= mUnreleased:0 <= mFrameCount:480), BufferSizeInFrames:480 11 12## Platforms Affected 13 14Android version 10 (Q) or earlier. 15 16Oboe version 1.4.0 or earlier when using OpenSLES with an OUTPUT stream callback. 17 18OR any version of Oboe if: 19* Oboe is using using OpenSL ES or a non-MMAP Legacy AAudio stream 20* AND you call stream->getFramesRead() or stream->getTimestamp(...) from inside 21an OUTPUT stream callback, 22 23It does **not** happen when Oboe uses AAudio MMAP because it does not call releaseBuffer(). 24 25## Workarounds 26 271. Use Oboe 1.4.1 or later. 281. Do not call stream->getFramesRead() or stream->getTimestamp() from inside the callback of an OUTPUT stream. If you absolutely must, then call them at the beginning of your callback to reduce the probability of a crash. 29 30Here is a [fix in Oboe 1.4.1](https://github.com/google/oboe/pull/863) that removed a call to getPosition(). 31 32## Root Cause 33 34The sequence of events is: 351. AudioFlinger AudioTrack obtains a buffer from the audio device 361. user plugs in headphones, which invalidates the audio device 371. app is called (callback) to render audio using the buffer 381. the app or Oboe calls getFramesRead() or getTimestamp(), which calls down to AudioTrack::getPosition() or AudioTrack::getTimestamp() 391. device routing change occurs because the audio device is [invalid](https://cs.android.com/android/platform/superproject/+/master:frameworks/av/media/libaudioclient/AudioTrack.cpp;l=1239;drc=48e98cf8dbd9fa212a0e129822929dc40e6c3898) 401. callback ends by releasing the buffer back to a different device 411. AudioTrackShared::releaseBuffer() checks to make sure the device matches the one in ObtainBuffer() and asserts if they do not match. 42 43Oboe, before V1.4.1, would update the server position in its callback. This called getPosition() in OpenSL ES, which called AudioTrack::getPosition(). 44 45The probability of the assert() is proportional to the time that the CPU spends between obtaining a buffer and calling restoreTrack_l(). 46 47This bug is tracked internally at: b/136268149 48 49## Reproduce the Bug 50 51These steps will trigger the bug most of the time: 52 531. Install OboeTester 1.5.22 or later, with Oboe < 1.4.1 541. Enter in a Terminal window: adb logcat | grep releaseBuffer 551. Launch OboeTester 561. Click TEST OUTPUT 571. Select API: OpenSL ES 581. Click OPEN 591. Click START, you should hear a tone 601. Slide "Workload" fader slowly up until you hear bad glitches. 611. Plug in headphones. 62 63You may see a message like this in the logcat: 64 65 AudioTrackShared: releaseBuffer: mUnreleased out of range, !(stepCount:96 <= mUnreleased:0 <= mFrameCount:480), BufferSizeInFrames:480 66 67# OEM Information 68 69These patches are available in Q AOSP: 701. [AudioTrack](https://android-review.googlesource.com/c/platform/frameworks/av/+/1251871/) 711. [AudioRecord](https://android-review.googlesource.com/c/platform/frameworks/av/+/1251872/) 72