Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/AudioBufferUtils.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#ifndef MOZILLA_SCRATCHBUFFER_H_
7
#define MOZILLA_SCRATCHBUFFER_H_
8
#include <mozilla/PodOperations.h>
9
#include <algorithm>
10
11
namespace mozilla {
12
13
/**
14
 * The classes in this file provide a interface that uses frames as a unit.
15
 * However, they store their offsets in samples (because it's handy for pointer
16
 * operations). Those functions can convert between the two units.
17
 */
18
0
static inline uint32_t FramesToSamples(uint32_t aChannels, uint32_t aFrames) {
19
0
  return aFrames * aChannels;
20
0
}
Unexecuted instantiation: Unified_cpp_src_mediapipeline0.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_src_peerconnection0.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media0.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media10.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media3.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media4.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media6.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media7.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media_webrtc0.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:mozilla::FramesToSamples(unsigned int, unsigned int)
21
22
0
static inline uint32_t SamplesToFrames(uint32_t aChannels, uint32_t aSamples) {
23
0
  MOZ_ASSERT(!(aSamples % aChannels), "Frame alignment is wrong.");
24
0
  return aSamples / aChannels;
25
0
}
Unexecuted instantiation: Unified_cpp_src_mediapipeline0.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_src_peerconnection0.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media0.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media10.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media3.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media4.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media6.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media7.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media_webaudio0.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media_webrtc0.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
Unexecuted instantiation: Unified_cpp_dom_media_gtest0.cpp:mozilla::SamplesToFrames(unsigned int, unsigned int)
26
27
/**
28
 * Class that gets a buffer pointer from an audio callback and provides a safe
29
 * interface to manipulate this buffer, and to ensure we are not missing frames
30
 * by the end of the callback.
31
 */
32
template<typename T>
33
class AudioCallbackBufferWrapper
34
{
35
public:
36
  AudioCallbackBufferWrapper()
37
    : mBuffer(nullptr)
38
    , mSamples(0)
39
    , mSampleWriteOffset(1)
40
    , mChannels(0)
41
0
  {}
42
43
  explicit AudioCallbackBufferWrapper(uint32_t aChannels)
44
    : mBuffer(nullptr)
45
    , mSamples(0)
46
    , mSampleWriteOffset(1)
47
    , mChannels(aChannels)
48
49
0
  {
50
0
    MOZ_ASSERT(aChannels);
51
0
  }
52
53
  AudioCallbackBufferWrapper& operator=(const AudioCallbackBufferWrapper& aOther)
54
0
  {
55
0
    MOZ_ASSERT(!aOther.mBuffer,
56
0
               "Don't use this ctor after AudioCallbackDriver::Init");
57
0
    MOZ_ASSERT(aOther.mSamples == 0,
58
0
               "Don't use this ctor after AudioCallbackDriver::Init");
59
0
    MOZ_ASSERT(aOther.mSampleWriteOffset == 1,
60
0
               "Don't use this ctor after AudioCallbackDriver::Init");
61
0
    MOZ_ASSERT(aOther.mChannels != 0);
62
0
63
0
    mBuffer = nullptr;
64
0
    mSamples = 0;
65
0
    mSampleWriteOffset = 1;
66
0
    mChannels = aOther.mChannels;
67
0
68
0
    return *this;
69
0
  }
70
71
  /**
72
   * Set the buffer in this wrapper. This is to be called at the beginning of
73
   * the callback.
74
   */
75
0
  void SetBuffer(T* aBuffer, uint32_t aFrames) {
76
0
    MOZ_ASSERT(!mBuffer && !mSamples,
77
0
        "SetBuffer called twice.");
78
0
    mBuffer = aBuffer;
79
0
    mSamples = FramesToSamples(mChannels, aFrames);
80
0
    mSampleWriteOffset = 0;
81
0
  }
82
83
  /**
84
   * Write some frames to the internal buffer. Free space in the buffer should
85
   * be check prior to calling this.
86
   */
87
0
  void WriteFrames(T* aBuffer, uint32_t aFrames) {
88
0
    MOZ_ASSERT(aFrames <= Available(),
89
0
        "Writing more that we can in the audio buffer.");
90
0
91
0
    PodCopy(mBuffer + mSampleWriteOffset, aBuffer, FramesToSamples(mChannels,
92
0
                                                                   aFrames));
93
0
    mSampleWriteOffset += FramesToSamples(mChannels, aFrames);
94
0
  }
95
96
  /**
97
   * Number of frames that can be written to the buffer.
98
   */
99
0
  uint32_t Available() {
100
0
    return SamplesToFrames(mChannels, mSamples - mSampleWriteOffset);
101
0
  }
102
103
  /**
104
   * Check that the buffer is completly filled, and reset internal state so this
105
   * instance can be reused.
106
   */
107
0
  void BufferFilled() {
108
0
    // It's okay to have exactly zero samples here, it can happen we have an
109
0
    // audio callback driver because of a hint on MSG creation, but the
110
0
    // AudioOutputStream has not been created yet, or if all the streams have finished
111
0
    // but we're still running.
112
0
    // Note: it's also ok if we had data in the scratch buffer - and we usually do - and
113
0
    // all the streams were ended (no mixer callback occured).
114
0
    // XXX Remove this warning, or find a way to avoid it if the mixer callback
115
0
    // isn't called.
116
0
    NS_WARNING_ASSERTION(
117
0
      Available() == 0 || mSampleWriteOffset == 0,
118
0
      "Audio Buffer is not full by the end of the callback.");
119
0
    // Make sure the data returned is always set and not random!
120
0
    if (Available()) {
121
0
      PodZero(mBuffer + mSampleWriteOffset, FramesToSamples(mChannels, Available()));
122
0
    }
123
0
    MOZ_ASSERT(mSamples, "Buffer not set.");
124
0
    mSamples = 0;
125
0
    mSampleWriteOffset = 0;
126
0
    mBuffer = nullptr;
127
0
  }
128
129
private:
130
  /* This is not an owned pointer, but the pointer passed to use via the audio
131
   * callback. */
132
  T* mBuffer;
133
  /* The number of samples of this audio buffer. */
134
  uint32_t mSamples;
135
  /* The position at which new samples should be written. We want to return to
136
   * the audio callback iff this is equal to mSamples. */
137
  uint32_t mSampleWriteOffset;
138
  uint32_t mChannels;
139
};
140
141
/**
142
 * This is a class that interfaces with the AudioCallbackBufferWrapper, and is
143
 * responsible for storing the excess of data produced by the MediaStreamGraph
144
 * because of different rounding constraints, to be used the next time the audio
145
 * backend calls back.
146
 */
147
template<typename T, uint32_t BLOCK_SIZE>
148
class SpillBuffer
149
{
150
public:
151
  SpillBuffer()
152
    : mBuffer(nullptr)
153
    , mPosition(0)
154
    , mChannels(0)
155
0
  {}
156
157
  explicit SpillBuffer(uint32_t aChannels)
158
  : mPosition(0)
159
  , mChannels(aChannels)
160
0
  {
161
0
    MOZ_ASSERT(aChannels);
162
0
    mBuffer = MakeUnique<T[]>(BLOCK_SIZE * mChannels);
163
0
    PodZero(mBuffer.get(), BLOCK_SIZE * mChannels);
164
0
  }
Unexecuted instantiation: mozilla::SpillBuffer<float, 256u>::SpillBuffer(unsigned int)
Unexecuted instantiation: mozilla::SpillBuffer<float, 128u>::SpillBuffer(unsigned int)
165
166
  SpillBuffer& operator=(SpillBuffer& aOther)
167
0
  {
168
0
    MOZ_ASSERT(aOther.mPosition == 0,
169
0
        "Don't use this ctor after AudioCallbackDriver::Init");
170
0
    MOZ_ASSERT(aOther.mChannels != 0);
171
0
    MOZ_ASSERT(aOther.mBuffer);
172
0
173
0
    mPosition = aOther.mPosition;
174
0
    mChannels = aOther.mChannels;
175
0
    mBuffer = std::move(aOther.mBuffer);
176
0
177
0
    return *this;
178
0
  }
179
180
  SpillBuffer& operator=(SpillBuffer&& aOther)
181
0
  {
182
0
    return this->operator=(aOther);
183
0
  }
184
185
  /* Empty the spill buffer into the buffer of the audio callback. This returns
186
   * the number of frames written. */
187
0
  uint32_t Empty(AudioCallbackBufferWrapper<T>& aBuffer) {
188
0
    uint32_t framesToWrite = std::min(aBuffer.Available(),
189
0
                                      SamplesToFrames(mChannels, mPosition));
190
0
191
0
    aBuffer.WriteFrames(mBuffer.get(), framesToWrite);
192
0
193
0
    mPosition -= FramesToSamples(mChannels, framesToWrite);
194
0
    // If we didn't empty the spill buffer for some reason, shift the remaining data down
195
0
    if (mPosition > 0) {
196
0
      MOZ_ASSERT(FramesToSamples(mChannels, framesToWrite) + mPosition <= BLOCK_SIZE * mChannels);
197
0
      PodMove(mBuffer.get(), mBuffer.get() + FramesToSamples(mChannels, framesToWrite),
198
0
              mPosition);
199
0
    }
200
0
201
0
    return framesToWrite;
202
0
  }
Unexecuted instantiation: mozilla::SpillBuffer<float, 256u>::Empty(mozilla::AudioCallbackBufferWrapper<float>&)
Unexecuted instantiation: mozilla::SpillBuffer<float, 128u>::Empty(mozilla::AudioCallbackBufferWrapper<float>&)
203
  /* Fill the spill buffer from aInput, containing aFrames frames, return the
204
   * number of frames written to the spill buffer */
205
0
  uint32_t Fill(T* aInput, uint32_t aFrames) {
206
0
    uint32_t framesToWrite = std::min(aFrames,
207
0
                                      BLOCK_SIZE - SamplesToFrames(mChannels,
208
0
                                                                   mPosition));
209
0
210
0
    MOZ_ASSERT(FramesToSamples(mChannels, framesToWrite) + mPosition <= BLOCK_SIZE * mChannels);
211
0
    PodCopy(mBuffer.get() + mPosition, aInput, FramesToSamples(mChannels,
212
0
                                                         framesToWrite));
213
0
214
0
    mPosition += FramesToSamples(mChannels, framesToWrite);
215
0
216
0
    return framesToWrite;
217
0
  }
Unexecuted instantiation: mozilla::SpillBuffer<float, 256u>::Fill(float*, unsigned int)
Unexecuted instantiation: mozilla::SpillBuffer<float, 128u>::Fill(float*, unsigned int)
218
private:
219
  /* The spilled data. */
220
  UniquePtr<T[]> mBuffer;
221
  /* The current write position, in samples, in the buffer when filling, or the
222
   * amount of buffer filled when emptying. */
223
  uint32_t mPosition;
224
  uint32_t mChannels;
225
};
226
227
} // namespace mozilla
228
229
#endif // MOZILLA_SCRATCHBUFFER_H_