summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.h
blob: e4ccdd647e65889f6eaa04e039069c341b480a3e (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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PUSH_PULL_FIFO_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PUSH_PULL_FIFO_H_

#include "third_party/blink/renderer/platform/audio/audio_bus.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/threading.h"
#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"

namespace blink {

// A configuration data container for PushPullFIFO unit test.
struct PushPullFIFOStateForTest {
  const size_t fifo_length;
  const unsigned number_of_channels;
  const size_t frames_available;
  const size_t index_read;
  const size_t index_write;
  const unsigned overflow_count;
  const unsigned underflow_count;
};

// PushPullFIFO class is an intermediate audio sample storage between
// Blink-WebAudio and the renderer. The renderer's hardware callback buffer size
// varies on the platform, but the WebAudio always renders 128 frames (render
// quantum, RQ) thus FIFO is needed to handle the general case.
//
// Note that this object is concurrently accessed by two threads; WebAudio
// rendering thread (WebThread) in Blink and the audio device thread
// (AudioDeviceThread) from the media renderer. The push/pull operations touch
// most of variables in the class (index_write_, index_read_, frames_available_,
// and fifo_Bus_) so the thread safety must be handled with care.
//
// TODO(hongchan): add a unit test for multi-thread access.
class PLATFORM_EXPORT PushPullFIFO {
  USING_FAST_MALLOC(PushPullFIFO);

 public:
  // Maximum FIFO length. (512 render quanta)
  static const uint32_t kMaxFIFOLength;

  // |fifo_length| cannot exceed |kMaxFIFOLength|. Otherwise it crashes.
  // ||render_quantum_frames| is the render size used by the audio graph.  It
  // |defaults to 128, the original and default render size.
  explicit PushPullFIFO(unsigned number_of_channels,
                        uint32_t fifo_length,
                        unsigned render_quantum_frames = 128);
  PushPullFIFO(const PushPullFIFO&) = delete;
  PushPullFIFO& operator=(const PushPullFIFO&) = delete;
  ~PushPullFIFO();

  // Pushes the rendered frames by WebAudio engine.
  //  - The |input_bus| length has a length equal to |render_quantum_frames_|,
  //  fixed.
  //  - In case of overflow (FIFO full while push), the existing frames in FIFO
  //    will be overwritten and |index_read_| will be forcibly moved to
  //    |index_write_| to avoid reading overwritten frames.
  void Push(const AudioBus* input_bus);

  // Pulls |frames_requested| by the audio device thread and returns the actual
  // number of frames to be rendered by the source. (i.e. WebAudio graph)
  //  - If |frames_requested| is bigger than the length of |output_bus|, it
  //    violates SECURITY_CHECK().
  //  - If |frames_requested| is bigger than FIFO length, it violates
  //    SECURITY_CHECK().
  //  - In case of underflow (FIFO empty while pull), the remaining space in the
  //    requested output bus will be filled with silence. Thus it will fulfill
  //    the request from the consumer without causing error, but with a glitch.
  size_t Pull(AudioBus* output_bus, uint32_t frames_requested);

  // Pull and update |ear_mark_frames_| to make the dual thread rendering mode
  // (i.e. AudioWorklet) more smooth. The single thread rendering does not need
  // this treatment.
  size_t PullAndUpdateEarmark(AudioBus* output_bus, uint32_t frames_requested);

  void SetEarmarkFrames(size_t earmark_frames) {
    DCHECK(IsMainThread());
    MutexLocker locker(lock_);
    earmark_frames_ = earmark_frames;
  }

  uint32_t length() const { return fifo_length_; }
  unsigned NumberOfChannels() const {
    lock_.AssertAcquired();
    return fifo_bus_->NumberOfChannels();
  }

  AudioBus* GetFIFOBusForTest() {
    MutexLocker locker(lock_);
    return fifo_bus_.get();
  }

  size_t GetEarmarkFramesForTest() {
    MutexLocker locker(lock_);
    return earmark_frames_;
  }

  // For single thread unit test only. Get the current configuration that
  // consists of FIFO length, number of channels, read/write index position and
  // under/overflow count.
  const PushPullFIFOStateForTest GetStateForTest();

 private:
  // The size of the FIFO.
  const uint32_t fifo_length_ = 0;

  // The render size used by the audio graph.
  const unsigned render_quantum_frames_;

  // For UMA reporting purpose.
  unsigned pull_count_ = 0;
  unsigned overflow_count_ = 0;
  unsigned underflow_count_ = 0;

  Mutex lock_;

  // To adapt the unstable callback timing. Every buffer underrun from
  // PullAndUpdateEarmark() will increase this number.
  size_t earmark_frames_ GUARDED_BY(lock_) = 0;

  // The number of frames in the FIFO actually available for pulling.
  uint32_t frames_available_ GUARDED_BY(lock_) = 0;
  size_t index_read_ GUARDED_BY(lock_) = 0;
  size_t index_write_ GUARDED_BY(lock_) = 0;
  scoped_refptr<AudioBus> fifo_bus_ GUARDED_BY(lock_);
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_PUSH_PULL_FIFO_H_