• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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