summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/sdk/android/src/java/org/webrtc/audio/LowLatencyAudioBufferManager.java
blob: 70c625ab4f1cf09414546d1fc26626a9117bb3a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/*
 *  Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

package org.webrtc.audio;

import android.media.AudioTrack;
import android.os.Build;
import org.webrtc.Logging;

// Lowers the buffer size if no underruns are detected for 100 ms. Once an
// underrun is detected, the buffer size is increased by 10 ms and it will not
// be lowered further. The buffer size will never be increased more than
// 5 times, to avoid the possibility of the buffer size increasing without
// bounds.
class LowLatencyAudioBufferManager {
  private static final String TAG = "LowLatencyAudioBufferManager";
  // The underrun count that was valid during the previous call to maybeAdjustBufferSize(). Used to
  // detect increases in the value.
  private int prevUnderrunCount;
  // The number of ticks to wait without an underrun before decreasing the buffer size.
  private int ticksUntilNextDecrease;
  // Indicate if we should continue to decrease the buffer size.
  private boolean keepLoweringBufferSize;
  // How often the buffer size was increased.
  private int bufferIncreaseCounter;

  public LowLatencyAudioBufferManager() {
    this.prevUnderrunCount = 0;
    this.ticksUntilNextDecrease = 10;
    this.keepLoweringBufferSize = true;
    this.bufferIncreaseCounter = 0;
  }

  public void maybeAdjustBufferSize(AudioTrack audioTrack) {
    if (Build.VERSION.SDK_INT >= 26) {
      final int underrunCount = audioTrack.getUnderrunCount();
      if (underrunCount > prevUnderrunCount) {
        // Don't increase buffer more than 5 times. Continuing to increase the buffer size
        // could be harmful on low-power devices that regularly experience underruns under
        // normal conditions.
        if (bufferIncreaseCounter < 5) {
          // Underrun detected, increase buffer size by 10ms.
          final int currentBufferSize = audioTrack.getBufferSizeInFrames();
          final int newBufferSize = currentBufferSize + audioTrack.getPlaybackRate() / 100;
          Logging.d(TAG,
              "Underrun detected! Increasing AudioTrack buffer size from " + currentBufferSize
                  + " to " + newBufferSize);
          audioTrack.setBufferSizeInFrames(newBufferSize);
          bufferIncreaseCounter++;
        }
        // Stop trying to lower the buffer size.
        keepLoweringBufferSize = false;
        prevUnderrunCount = underrunCount;
        ticksUntilNextDecrease = 10;
      } else if (keepLoweringBufferSize) {
        ticksUntilNextDecrease--;
        if (ticksUntilNextDecrease <= 0) {
          // No underrun seen for 100 ms, try to lower the buffer size by 10ms.
          final int bufferSize10ms = audioTrack.getPlaybackRate() / 100;
          // Never go below a buffer size of 10ms.
          final int currentBufferSize = audioTrack.getBufferSizeInFrames();
          final int newBufferSize = Math.max(bufferSize10ms, currentBufferSize - bufferSize10ms);
          if (newBufferSize != currentBufferSize) {
            Logging.d(TAG,
                "Lowering AudioTrack buffer size from " + currentBufferSize + " to "
                    + newBufferSize);
            audioTrack.setBufferSizeInFrames(newBufferSize);
          }
          ticksUntilNextDecrease = 10;
        }
      }
    }
  }
}