Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gtest/TestAudioTrackEncoder.cpp
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
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "gtest/gtest.h"
7
#include "OpusTrackEncoder.h"
8
#include "SineWaveGenerator.h"
9
10
using namespace mozilla;
11
12
class AudioGenerator
13
{
14
public:
15
  AudioGenerator(int32_t aChannels, int32_t aSampleRate)
16
    : mGenerator(aSampleRate, 1000)
17
    , mChannels(aChannels)
18
0
  {}
19
20
  void Generate(AudioSegment& aSegment, const int32_t& aSamples)
21
0
  {
22
0
    RefPtr<SharedBuffer> buffer = SharedBuffer::Create(aSamples * sizeof(int16_t));
23
0
    int16_t* dest = static_cast<int16_t*>(buffer->Data());
24
0
    mGenerator.generate(dest, aSamples);
25
0
    AutoTArray<const int16_t*, 1> channels;
26
0
    for (int32_t i = 0; i < mChannels; i++) {
27
0
      channels.AppendElement(dest);
28
0
    }
29
0
    aSegment.AppendFrames(buffer.forget(), channels, aSamples, PRINCIPAL_HANDLE_NONE);
30
0
  }
31
32
private:
33
  SineWaveGenerator mGenerator;
34
  const int32_t mChannels;
35
};
36
37
class TestOpusTrackEncoder : public OpusTrackEncoder
38
{
39
public:
40
0
  TestOpusTrackEncoder() : OpusTrackEncoder(90000) {}
41
42
  // Return true if it has successfully initialized the Opus encoder.
43
  bool TestOpusRawCreation(int aChannels, int aSamplingRate)
44
0
  {
45
0
    if (Init(aChannels, aSamplingRate) == NS_OK) {
46
0
      if (IsInitialized()) {
47
0
        return true;
48
0
      }
49
0
    }
50
0
    return false;
51
0
  }
52
53
  // Return the sample rate of data to be fed to the Opus encoder, could be
54
  // re-sampled if it was not one of the Opus supported sampling rates.
55
  // Init() is expected to be called first.
56
  int TestGetOutputSampleRate()
57
0
  {
58
0
    return mInitialized ? GetOutputSampleRate() : 0;
59
0
  }
60
};
61
62
static bool
63
TestOpusInit(int aChannels, int aSamplingRate)
64
0
{
65
0
  TestOpusTrackEncoder encoder;
66
0
  return encoder.TestOpusRawCreation(aChannels, aSamplingRate);
67
0
}
68
69
TEST(OpusAudioTrackEncoder, InitRaw)
70
0
{
71
0
  // Expect false with 0 or negative channels of input signal.
72
0
  EXPECT_FALSE(TestOpusInit(0, 16000));
73
0
  EXPECT_FALSE(TestOpusInit(-1, 16000));
74
0
75
0
  // The Opus format supports up to 8 channels, and supports multitrack audio up
76
0
  // to 255 channels, but the current implementation supports only mono and
77
0
  // stereo, and downmixes any more than that.
78
0
  // Expect false with channels of input signal exceed the max supported number.
79
0
  EXPECT_FALSE(TestOpusInit(8 + 1, 16000));
80
0
81
0
  // Should accept channels within valid range.
82
0
  for (int i = 1; i <= 8; i++) {
83
0
    EXPECT_TRUE(TestOpusInit(i, 16000));
84
0
  }
85
0
86
0
  // Expect false with 0 or negative sampling rate of input signal.
87
0
  EXPECT_FALSE(TestOpusInit(1, 0));
88
0
  EXPECT_FALSE(TestOpusInit(1, -1));
89
0
90
0
  // Verify sample rate bounds checking.
91
0
  EXPECT_FALSE(TestOpusInit(2, 2000));
92
0
  EXPECT_FALSE(TestOpusInit(2, 4000));
93
0
  EXPECT_FALSE(TestOpusInit(2, 7999));
94
0
  EXPECT_TRUE(TestOpusInit(2, 8000));
95
0
  EXPECT_TRUE(TestOpusInit(2, 192000));
96
0
  EXPECT_FALSE(TestOpusInit(2, 192001));
97
0
  EXPECT_FALSE(TestOpusInit(2, 200000));
98
0
}
99
100
TEST(OpusAudioTrackEncoder, Init)
101
0
{
102
0
  {
103
0
    // The encoder does not normally recieve enough info from null data to
104
0
    // init. However, multiple attempts to do so, with sufficiently long
105
0
    // duration segments, should result in a best effort attempt. The first
106
0
    // attempt should never do this though, even if the duration is long:
107
0
    OpusTrackEncoder encoder(48000);
108
0
    AudioSegment segment;
109
0
    segment.AppendNullData(48000 * 100);
110
0
    encoder.TryInit(segment, segment.GetDuration());
111
0
    EXPECT_FALSE(encoder.IsInitialized());
112
0
113
0
    // Multiple init attempts should result in best effort init:
114
0
    encoder.TryInit(segment, segment.GetDuration());
115
0
    EXPECT_TRUE(encoder.IsInitialized());
116
0
  }
117
0
118
0
  {
119
0
    // If the duration of the segments given to the encoder is not long then
120
0
    // we shouldn't try a best effort init:
121
0
    OpusTrackEncoder encoder(48000);
122
0
    AudioSegment segment;
123
0
    segment.AppendNullData(1);
124
0
    encoder.TryInit(segment, segment.GetDuration());
125
0
    EXPECT_FALSE(encoder.IsInitialized());
126
0
    encoder.TryInit(segment, segment.GetDuration());
127
0
    EXPECT_FALSE(encoder.IsInitialized());
128
0
  }
129
0
130
0
  {
131
0
    // For non-null segments we should init immediately
132
0
    OpusTrackEncoder encoder(48000);
133
0
    AudioSegment segment;
134
0
    AudioGenerator generator(2, 48000);
135
0
    generator.Generate(segment, 1);
136
0
    encoder.TryInit(segment, segment.GetDuration());
137
0
    EXPECT_TRUE(encoder.IsInitialized());
138
0
  }
139
0
140
0
  {
141
0
    // Test low sample rate bound
142
0
    OpusTrackEncoder encoder(7999);
143
0
    AudioSegment segment;
144
0
    AudioGenerator generator(2, 7999);
145
0
    generator.Generate(segment, 1);
146
0
    encoder.TryInit(segment, segment.GetDuration());
147
0
    EXPECT_FALSE(encoder.IsInitialized());
148
0
  }
149
0
150
0
  {
151
0
    // Test low sample rate bound
152
0
    OpusTrackEncoder encoder(8000);
153
0
    AudioSegment segment;
154
0
    AudioGenerator generator(2, 8000);
155
0
    generator.Generate(segment, 1);
156
0
    encoder.TryInit(segment, segment.GetDuration());
157
0
    EXPECT_TRUE(encoder.IsInitialized());
158
0
  }
159
0
160
0
  {
161
0
    // Test high sample rate bound
162
0
    OpusTrackEncoder encoder(192001);
163
0
    AudioSegment segment;
164
0
    AudioGenerator generator(2, 192001);
165
0
    generator.Generate(segment, 1);
166
0
    encoder.TryInit(segment, segment.GetDuration());
167
0
    EXPECT_FALSE(encoder.IsInitialized());
168
0
  }
169
0
170
0
  {
171
0
    // Test high sample rate bound
172
0
    OpusTrackEncoder encoder(192000);
173
0
    AudioSegment segment;
174
0
    AudioGenerator generator(2, 192000);
175
0
    generator.Generate(segment, 1);
176
0
    encoder.TryInit(segment, segment.GetDuration());
177
0
    EXPECT_TRUE(encoder.IsInitialized());
178
0
  }
179
0
}
180
181
static int
182
TestOpusResampler(int aChannels, int aSamplingRate)
183
0
{
184
0
  TestOpusTrackEncoder encoder;
185
0
  EXPECT_TRUE(encoder.TestOpusRawCreation(aChannels, aSamplingRate));
186
0
  return encoder.TestGetOutputSampleRate();
187
0
}
188
189
TEST(OpusAudioTrackEncoder, Resample)
190
0
{
191
0
  // Sampling rates of data to be fed to Opus encoder, should remain unchanged
192
0
  // if it is one of Opus supported rates (8000, 12000, 16000, 24000 and 48000
193
0
  // (kHz)) at initialization.
194
0
  EXPECT_TRUE(TestOpusResampler(1, 8000) == 8000);
195
0
  EXPECT_TRUE(TestOpusResampler(1, 12000) == 12000);
196
0
  EXPECT_TRUE(TestOpusResampler(1, 16000) == 16000);
197
0
  EXPECT_TRUE(TestOpusResampler(1, 24000) == 24000);
198
0
  EXPECT_TRUE(TestOpusResampler(1, 48000) == 48000);
199
0
200
0
  // Otherwise, it should be resampled to 48kHz by resampler.
201
0
  EXPECT_TRUE(TestOpusResampler(1, 9600) == 48000);
202
0
  EXPECT_TRUE(TestOpusResampler(1, 44100) == 48000);
203
0
}
204
205
TEST(OpusAudioTrackEncoder, FetchMetadata)
206
0
{
207
0
  const int32_t channels = 1;
208
0
  const int32_t sampleRate = 44100;
209
0
  TestOpusTrackEncoder encoder;
210
0
  EXPECT_TRUE(encoder.TestOpusRawCreation(channels, sampleRate));
211
0
212
0
  RefPtr<TrackMetadataBase> metadata = encoder.GetMetadata();
213
0
  ASSERT_EQ(TrackMetadataBase::METADATA_OPUS, metadata->GetKind());
214
0
215
0
  RefPtr<OpusMetadata> opusMeta =
216
0
    static_cast<OpusMetadata*>(metadata.get());
217
0
  EXPECT_EQ(channels, opusMeta->mChannels);
218
0
  EXPECT_EQ(sampleRate, opusMeta->mSamplingFrequency);
219
0
}
220
221
TEST(OpusAudioTrackEncoder, FrameEncode)
222
0
{
223
0
  const int32_t channels = 1;
224
0
  const int32_t sampleRate = 44100;
225
0
  TestOpusTrackEncoder encoder;
226
0
  EXPECT_TRUE(encoder.TestOpusRawCreation(channels, sampleRate));
227
0
228
0
  // Generate five seconds of raw audio data.
229
0
  AudioGenerator generator(channels, sampleRate);
230
0
  AudioSegment segment;
231
0
  const int32_t samples = sampleRate * 5;
232
0
  generator.Generate(segment, samples);
233
0
234
0
  encoder.SetStartOffset(0);
235
0
  encoder.AppendAudioSegment(std::move(segment));
236
0
  encoder.AdvanceCurrentTime(samples);
237
0
238
0
  EncodedFrameContainer container;
239
0
  EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container)));
240
0
241
0
  // Verify that encoded data is 5 seconds long.
242
0
  uint64_t totalDuration = 0;
243
0
  for (auto& frame : container.GetEncodedFrames()) {
244
0
    totalDuration += frame->GetDuration();
245
0
  }
246
0
  // 44100 as used above gets resampled to 48000 for opus.
247
0
  const uint64_t five = 48000 * 5;
248
0
  EXPECT_EQ(five, totalDuration);
249
0
}