oboe
                                
                                 oboe copied to clipboard
                                
                                    oboe copied to clipboard
                            
                            
                            
                        Adjust OpenSL ES buffer queue length based on capacity
Change kBufferQueueLength constant to mBufferQueueLength variable. Set mBufferQueueLength based on capacity and callbacksize.
See experiment on the effect of kBufferQueueLength on latency in: Why Oboe only enqueue one buffer? #464
Understanding FramesPerCallback / kBufferQueueLength / Buffer Size #736
According to this internal bug:
b/27819623 | Allow OpenSL ES to specify a total buffer size for a fast track
starting in N, adding more buffers would improve glitch protection. So this change would help devices running 7.0, 7.1 and 8.0. That would be about 1/3 of Android devices. For 8.1 and later, AAudio is available.
[Update: as of August 2022, 17.5% of devices are running < 8.1]
Allow OpenSL ES to specify a total buffer size for a fast track Has this feature implemented in branch 1.3-stable?
No. This issue is still open.
Measure the effect of changing kBufferQueueLength on Sargo running SP2A with OpenSL ES with Low Latency. Burst = 192.
| kBufferQueueLength | RT latency | Capacity | 
|---|---|---|
| 2 | 22.2 | |
| 3 | 24.1 | 576 | 
| 4 | 25.9 | 768 | 
Latency increases by 2 msec (96 frames) per buffer. Not 192!
Used main Oboetester screen to set frames per callback to 96.
| kBufferQueueLength | RT latency | Capacity | 
|---|---|---|
| 2 | 17.8 | 192 | 
| 3 | 19.9 | 288 | 
| 4 | 22.2 | 384 | 
The latency still increases by 2 msec/buffer but the latency is lower!
I then set kBufferQueueLength and added some code to the beginning of processBufferCallback(). Burst = 192. if (getBufferDepth(bq) >= 3) { return false; }
| maxDepth | RT latency | Capacity | 
|---|---|---|
| 3 | 34.2 | 1536 | 
| 4 | 34.0 | 1536 | 
| 5 | 34.27 | 1536 | 
This does not seem to affect latency! I expected it to limit the amount of data in the buffer. I added some logs and the depth was always zero.
I then added a loop in processBufferCallback:
while (depth < 4 && countdown-- > 0 && !shouldStopStream) {
In the first callback I could see the depth grow to four then stabilize.
| maxDepth | RT latency | Capacity | 
|---|---|---|
| 1 | 34.4 | 1536 | 
| 2 | 37.8 | 1536 | 
| 3 | 42.2 | 1536 | 
| 4 | 45.8 | 1536 | 
| 5 | 50.6 | 1536 | 
| 6 | 54.3 | 1536 | 
| 7 | 58.0 | 1536 | 
| 8 | 62.3 | 1536 | 
At 9 it failed to start because there were only 8 buffers allocated.
Hypothesis: in Android S the kBufferQueueLength affects the size of the internal buffer, which increases latency and should afford some glitch protection. Enqueuing multiple buffers increases the queue depth, adds latency. But I suspect these buffers are stored on the client side and will not help with glitch protection.
Testing on a Nexus 6 running SDK 6.0.1, burst = 192. Use format = PCM_I16 for input and output, analog headset jack.
| kBufferQueueLength | RT latency | Capacity | 
|---|---|---|
| 2 | 50.0 | 384 | 
| 3 | 49.5 | 576 | 
| 4 | 49.7 | 768 | 
Latency, and glitch protection, does not increase with the number of buffers. That was added in 7.0. ag/941250 SDK 6.0 has problems that we cannot fix in Oboe.
Testing on a Nexus 6 running SDK 7 MR2, burst = 192. Use format = PCM_I16 for input and output, analog headset jack.
| kBufferQueueLength | RT latency | Capacity | 
|---|---|---|
| 2 | 53.3 | 384 | 
| 3 | 57.5 | 576 | 
| 4 | 61.5 | 768 | 
Latency, and glitch protection, increases with the number of buffers.