1 /* 2 * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc.audio; 12 13 import android.media.AudioTrack; 14 import android.os.Build; 15 import org.webrtc.Logging; 16 17 // Lowers the buffer size if no underruns are detected for 100 ms. Once an 18 // underrun is detected, the buffer size is increased by 10 ms and it will not 19 // be lowered further. The buffer size will never be increased more than 20 // 5 times, to avoid the possibility of the buffer size increasing without 21 // bounds. 22 class LowLatencyAudioBufferManager { 23 private static final String TAG = "LowLatencyAudioBufferManager"; 24 // The underrun count that was valid during the previous call to maybeAdjustBufferSize(). Used to 25 // detect increases in the value. 26 private int prevUnderrunCount; 27 // The number of ticks to wait without an underrun before decreasing the buffer size. 28 private int ticksUntilNextDecrease; 29 // Indicate if we should continue to decrease the buffer size. 30 private boolean keepLoweringBufferSize; 31 // How often the buffer size was increased. 32 private int bufferIncreaseCounter; 33 LowLatencyAudioBufferManager()34 public LowLatencyAudioBufferManager() { 35 this.prevUnderrunCount = 0; 36 this.ticksUntilNextDecrease = 10; 37 this.keepLoweringBufferSize = true; 38 this.bufferIncreaseCounter = 0; 39 } 40 maybeAdjustBufferSize(AudioTrack audioTrack)41 public void maybeAdjustBufferSize(AudioTrack audioTrack) { 42 if (Build.VERSION.SDK_INT >= 26) { 43 final int underrunCount = audioTrack.getUnderrunCount(); 44 if (underrunCount > prevUnderrunCount) { 45 // Don't increase buffer more than 5 times. Continuing to increase the buffer size 46 // could be harmful on low-power devices that regularly experience underruns under 47 // normal conditions. 48 if (bufferIncreaseCounter < 5) { 49 // Underrun detected, increase buffer size by 10ms. 50 final int currentBufferSize = audioTrack.getBufferSizeInFrames(); 51 final int newBufferSize = currentBufferSize + audioTrack.getPlaybackRate() / 100; 52 Logging.d(TAG, 53 "Underrun detected! Increasing AudioTrack buffer size from " + currentBufferSize 54 + " to " + newBufferSize); 55 audioTrack.setBufferSizeInFrames(newBufferSize); 56 bufferIncreaseCounter++; 57 } 58 // Stop trying to lower the buffer size. 59 keepLoweringBufferSize = false; 60 prevUnderrunCount = underrunCount; 61 ticksUntilNextDecrease = 10; 62 } else if (keepLoweringBufferSize) { 63 ticksUntilNextDecrease--; 64 if (ticksUntilNextDecrease <= 0) { 65 // No underrun seen for 100 ms, try to lower the buffer size by 10ms. 66 final int bufferSize10ms = audioTrack.getPlaybackRate() / 100; 67 // Never go below a buffer size of 10ms. 68 final int currentBufferSize = audioTrack.getBufferSizeInFrames(); 69 final int newBufferSize = Math.max(bufferSize10ms, currentBufferSize - bufferSize10ms); 70 if (newBufferSize != currentBufferSize) { 71 Logging.d(TAG, 72 "Lowering AudioTrack buffer size from " + currentBufferSize + " to " 73 + newBufferSize); 74 audioTrack.setBufferSizeInFrames(newBufferSize); 75 } 76 ticksUntilNextDecrease = 10; 77 } 78 } 79 } 80 } 81 } 82