Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/AudioPacketizer.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 AudioPacketizer_h_
7
#define AudioPacketizer_h_
8
9
#include <mozilla/PodOperations.h>
10
#include <mozilla/Assertions.h>
11
#include <mozilla/UniquePtr.h>
12
#include <AudioSampleFormat.h>
13
14
// Enable this to warn when `Output` has been called but not enough data was
15
// buffered.
16
// #define LOG_PACKETIZER_UNDERRUN
17
18
namespace mozilla {
19
/**
20
 * This class takes arbitrary input data, and returns packets of a specific
21
 * size. In the process, it can convert audio samples from 16bit integers to
22
 * float (or vice-versa).
23
 *
24
 * Input and output, as well as length units in the public interface are
25
 * interleaved frames.
26
 *
27
 * Allocations of output buffer can be performed by this class.  Buffers can
28
 * simply be delete-d.  This is because packets are intended to be sent off to
29
 * non-gecko code using normal pointers/length pairs
30
 *
31
 * Alternatively, consumers can pass in a buffer in which the output is copied.
32
 * The buffer needs to be large enough to store a packet worth of audio.
33
 *
34
 * The implementation uses a circular buffer using absolute virtual indices.
35
 */
36
template <typename InputType, typename OutputType>
37
class AudioPacketizer
38
{
39
public:
40
  AudioPacketizer(uint32_t aPacketSize, uint32_t aChannels)
41
    : mPacketSize(aPacketSize)
42
    , mChannels(aChannels)
43
    , mReadIndex(0)
44
    , mWriteIndex(0)
45
    // Start off with a single packet
46
    , mStorage(new InputType[aPacketSize * aChannels])
47
    , mLength(aPacketSize * aChannels)
48
0
  {
49
0
     MOZ_ASSERT(aPacketSize > 0 && aChannels > 0,
50
0
       "The packet size and the number of channel should be strictly positive");
51
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::AudioPacketizer(unsigned int, unsigned int)
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::AudioPacketizer(unsigned int, unsigned int)
52
53
  void Input(const InputType* aFrames, uint32_t aFrameCount)
54
0
  {
55
0
    uint32_t inputSamples = aFrameCount * mChannels;
56
0
    // Need to grow the storage. This should rarely happen, if at all, once the
57
0
    // array has the right size.
58
0
    if (inputSamples > EmptySlots()) {
59
0
      // Calls to Input and Output are roughtly interleaved
60
0
      // (Input,Output,Input,Output, etc.), or balanced
61
0
      // (Input,Input,Input,Output,Output,Output), so we update the buffer to
62
0
      // the exact right size in order to not waste space.
63
0
      uint32_t newLength = AvailableSamples() + inputSamples;
64
0
      uint32_t toCopy = AvailableSamples();
65
0
      UniquePtr<InputType[]> oldStorage = std::move(mStorage);
66
0
      mStorage = mozilla::MakeUnique<InputType[]>(newLength);
67
0
      // Copy the old data at the beginning of the new storage.
68
0
      if (WriteIndex() >= ReadIndex()) {
69
0
        PodCopy(mStorage.get(),
70
0
                oldStorage.get() + ReadIndex(),
71
0
                AvailableSamples());
72
0
      } else {
73
0
        uint32_t firstPartLength = mLength - ReadIndex();
74
0
        uint32_t secondPartLength = AvailableSamples() - firstPartLength;
75
0
        PodCopy(mStorage.get(),
76
0
                oldStorage.get() + ReadIndex(),
77
0
                firstPartLength);
78
0
        PodCopy(mStorage.get() + firstPartLength,
79
0
                oldStorage.get(),
80
0
                secondPartLength);
81
0
      }
82
0
      mWriteIndex = toCopy;
83
0
      mReadIndex = 0;
84
0
      mLength = newLength;
85
0
    }
86
0
87
0
    if (WriteIndex() + inputSamples <= mLength) {
88
0
      PodCopy(mStorage.get() + WriteIndex(), aFrames, aFrameCount * mChannels);
89
0
    } else {
90
0
      uint32_t firstPartLength = mLength - WriteIndex();
91
0
      uint32_t secondPartLength = inputSamples - firstPartLength;
92
0
      PodCopy(mStorage.get() + WriteIndex(), aFrames, firstPartLength);
93
0
      PodCopy(mStorage.get(), aFrames + firstPartLength, secondPartLength);
94
0
    }
95
0
96
0
    mWriteIndex += inputSamples;
97
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::Input(short const*, unsigned int)
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::Input(float const*, unsigned int)
98
99
  OutputType* Output()
100
0
  {
101
0
    uint32_t samplesNeeded = mPacketSize * mChannels;
102
0
    OutputType* out = new OutputType[samplesNeeded];
103
0
104
0
    Output(out);
105
0
106
0
    return out;
107
0
  }
108
109
  void Output(OutputType* aOutputBuffer)
110
0
  {
111
0
    uint32_t samplesNeeded = mPacketSize * mChannels;
112
0
113
0
    // Under-run. Pad the end of the buffer with silence.
114
0
    if (AvailableSamples() < samplesNeeded) {
115
#ifdef LOG_PACKETIZER_UNDERRUN
116
      char buf[256];
117
      snprintf(buf, 256,
118
               "AudioPacketizer %p underrun: available: %u, needed: %u\n",
119
               this, AvailableSamples(), samplesNeeded);
120
      NS_WARNING(buf);
121
#endif
122
      uint32_t zeros = samplesNeeded - AvailableSamples();
123
0
      PodZero(aOutputBuffer + AvailableSamples(), zeros);
124
0
      samplesNeeded -= zeros;
125
0
    }
126
0
    if (ReadIndex() + samplesNeeded <= mLength) {
127
0
      ConvertAudioSamples<InputType,OutputType>(mStorage.get() + ReadIndex(),
128
0
                                                aOutputBuffer,
129
0
                                                samplesNeeded);
130
0
    } else {
131
0
      uint32_t firstPartLength = mLength - ReadIndex();
132
0
      uint32_t secondPartLength = samplesNeeded - firstPartLength;
133
0
      ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(),
134
0
                                                 aOutputBuffer,
135
0
                                                 firstPartLength);
136
0
      ConvertAudioSamples<InputType, OutputType>(mStorage.get(),
137
0
                                                 aOutputBuffer + firstPartLength,
138
0
                                                 secondPartLength);
139
0
    }
140
0
    mReadIndex += samplesNeeded;
141
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::Output(short*)
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::Output(float*)
142
143
0
  uint32_t PacketsAvailable() const {
144
0
    return AvailableSamples() / mChannels / mPacketSize;
145
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::PacketsAvailable() const
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::PacketsAvailable() const
146
147
  bool Empty() const {
148
   return mWriteIndex == mReadIndex;
149
  }
150
151
  bool Full() const {
152
    return mWriteIndex - mReadIndex == mLength;
153
  }
154
155
0
  uint32_t PacketSize() const {
156
0
    return mPacketSize;
157
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::PacketSize() const
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::PacketSize() const
158
159
0
  uint32_t Channels() const {
160
0
    return mChannels;
161
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::Channels() const
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::Channels() const
162
163
private:
164
0
  uint32_t ReadIndex() const {
165
0
    return mReadIndex % mLength;
166
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::ReadIndex() const
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::ReadIndex() const
167
168
0
  uint32_t WriteIndex() const {
169
0
    return mWriteIndex % mLength;
170
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::WriteIndex() const
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::WriteIndex() const
171
172
0
  uint32_t AvailableSamples() const {
173
0
    return mWriteIndex - mReadIndex;
174
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::AvailableSamples() const
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::AvailableSamples() const
175
176
0
  uint32_t EmptySlots() const {
177
0
    return mLength - AvailableSamples();
178
0
  }
Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::EmptySlots() const
Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::EmptySlots() const
179
180
  // Size of one packet of audio, in frames
181
  uint32_t mPacketSize;
182
  // Number of channels of the stream flowing through this packetizer
183
  uint32_t mChannels;
184
  // Two virtual index into the buffer: the read position and the write
185
  // position.
186
  uint64_t mReadIndex;
187
  uint64_t mWriteIndex;
188
  // Storage for the samples
189
  mozilla::UniquePtr<InputType[]> mStorage;
190
  // Length of the buffer, in samples
191
  uint32_t mLength;
192
};
193
194
} // mozilla
195
196
#endif // AudioPacketizer_h_