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 
9 #include "AudioGenerator.h"
10 #include "AudioSampleFormat.h"
11 
12 using namespace mozilla;
13 
14 class TestOpusTrackEncoder : public OpusTrackEncoder {
15  public:
TestOpusTrackEncoder(TrackRate aTrackRate,MediaQueue<EncodedFrame> & aEncodedDataQueue)16   TestOpusTrackEncoder(TrackRate aTrackRate,
17                        MediaQueue<EncodedFrame>& aEncodedDataQueue)
18       : OpusTrackEncoder(aTrackRate, aEncodedDataQueue) {}
19 
20   // Return true if it has successfully initialized the Opus encoder.
TestOpusRawCreation(int aChannels)21   bool TestOpusRawCreation(int aChannels) {
22     if (Init(aChannels) == NS_OK) {
23       if (IsInitialized()) {
24         return true;
25       }
26     }
27     return false;
28   }
29 };
30 
TestOpusInit(int aChannels,TrackRate aSamplingRate)31 static bool TestOpusInit(int aChannels, TrackRate aSamplingRate) {
32   MediaQueue<EncodedFrame> frames;
33   TestOpusTrackEncoder encoder(aSamplingRate, frames);
34   return encoder.TestOpusRawCreation(aChannels);
35 }
36 
TEST(OpusAudioTrackEncoder,InitRaw)37 TEST(OpusAudioTrackEncoder, InitRaw)
38 {
39   // Expect false with 0 or negative channels of input signal.
40   EXPECT_FALSE(TestOpusInit(0, 16000));
41   EXPECT_FALSE(TestOpusInit(-1, 16000));
42 
43   // The Opus format supports up to 8 channels, and supports multitrack audio up
44   // to 255 channels, but the current implementation supports only mono and
45   // stereo, and downmixes any more than that.
46   // Expect false with channels of input signal exceed the max supported number.
47   EXPECT_FALSE(TestOpusInit(8 + 1, 16000));
48 
49   // Should accept channels within valid range.
50   for (int i = 1; i <= 8; i++) {
51     EXPECT_TRUE(TestOpusInit(i, 16000));
52   }
53 
54   // Expect false with 0 or negative sampling rate of input signal.
55   EXPECT_FALSE(TestOpusInit(1, 0));
56   EXPECT_FALSE(TestOpusInit(1, -1));
57 
58   // Verify sample rate bounds checking.
59   EXPECT_FALSE(TestOpusInit(2, 2000));
60   EXPECT_FALSE(TestOpusInit(2, 4000));
61   EXPECT_FALSE(TestOpusInit(2, 7999));
62   EXPECT_TRUE(TestOpusInit(2, 8000));
63   EXPECT_TRUE(TestOpusInit(2, 192000));
64   EXPECT_FALSE(TestOpusInit(2, 192001));
65   EXPECT_FALSE(TestOpusInit(2, 200000));
66 }
67 
TEST(OpusAudioTrackEncoder,Init)68 TEST(OpusAudioTrackEncoder, Init)
69 {
70   {
71     // The encoder does not normally recieve enough info from null data to
72     // init. However, multiple attempts to do so, with sufficiently long
73     // duration segments, should result in a default-init. The first attempt
74     // should never do this though, even if the duration is long:
75     MediaQueue<EncodedFrame> frames;
76     OpusTrackEncoder encoder(48000, frames);
77     AudioSegment segment;
78     segment.AppendNullData(48000 * 100);
79     encoder.TryInit(segment, segment.GetDuration());
80     EXPECT_FALSE(encoder.IsInitialized());
81 
82     // Multiple init attempts should result in best effort init:
83     encoder.TryInit(segment, segment.GetDuration());
84     EXPECT_TRUE(encoder.IsInitialized());
85   }
86 
87   {
88     // For non-null segments we should init immediately
89     MediaQueue<EncodedFrame> frames;
90     OpusTrackEncoder encoder(48000, frames);
91     AudioSegment segment;
92     AudioGenerator<AudioDataValue> generator(2, 48000);
93     generator.Generate(segment, 1);
94     encoder.TryInit(segment, segment.GetDuration());
95     EXPECT_TRUE(encoder.IsInitialized());
96   }
97 
98   {
99     // Test low sample rate bound
100     MediaQueue<EncodedFrame> frames;
101     OpusTrackEncoder encoder(7999, frames);
102     AudioSegment segment;
103     AudioGenerator<AudioDataValue> generator(2, 7999);
104     generator.Generate(segment, 1);
105     encoder.TryInit(segment, segment.GetDuration());
106     EXPECT_FALSE(encoder.IsInitialized());
107   }
108 
109   {
110     // Test low sample rate bound
111     MediaQueue<EncodedFrame> frames;
112     OpusTrackEncoder encoder(8000, frames);
113     AudioSegment segment;
114     AudioGenerator<AudioDataValue> generator(2, 8000);
115     generator.Generate(segment, 1);
116     encoder.TryInit(segment, segment.GetDuration());
117     EXPECT_TRUE(encoder.IsInitialized());
118   }
119 
120   {
121     // Test high sample rate bound
122     MediaQueue<EncodedFrame> frames;
123     OpusTrackEncoder encoder(192001, frames);
124     AudioSegment segment;
125     AudioGenerator<AudioDataValue> generator(2, 192001);
126     generator.Generate(segment, 1);
127     encoder.TryInit(segment, segment.GetDuration());
128     EXPECT_FALSE(encoder.IsInitialized());
129   }
130 
131   {
132     // Test high sample rate bound
133     MediaQueue<EncodedFrame> frames;
134     OpusTrackEncoder encoder(192000, frames);
135     AudioSegment segment;
136     AudioGenerator<AudioDataValue> generator(2, 192000);
137     generator.Generate(segment, 1);
138     encoder.TryInit(segment, segment.GetDuration());
139     EXPECT_TRUE(encoder.IsInitialized());
140   }
141 
142   {
143     // Test that it takes 10s to trigger default-init.
144     MediaQueue<EncodedFrame> frames;
145     OpusTrackEncoder encoder(48000, frames);
146     AudioSegment longSegment;
147     longSegment.AppendNullData(48000 * 10 - 1);
148     AudioSegment shortSegment;
149     shortSegment.AppendNullData(1);
150     encoder.TryInit(longSegment, longSegment.GetDuration());
151     EXPECT_FALSE(encoder.IsInitialized());
152     encoder.TryInit(shortSegment, shortSegment.GetDuration());
153     EXPECT_FALSE(encoder.IsInitialized());
154     encoder.TryInit(shortSegment, shortSegment.GetDuration());
155     EXPECT_TRUE(encoder.IsInitialized());
156   }
157 }
158 
TestOpusResampler(TrackRate aSamplingRate)159 static int TestOpusResampler(TrackRate aSamplingRate) {
160   MediaQueue<EncodedFrame> frames;
161   OpusTrackEncoder encoder(aSamplingRate, frames);
162   return encoder.mOutputSampleRate;
163 }
164 
TEST(OpusAudioTrackEncoder,Resample)165 TEST(OpusAudioTrackEncoder, Resample)
166 {
167   // Sampling rates of data to be fed to Opus encoder, should remain unchanged
168   // if it is one of Opus supported rates (8000, 12000, 16000, 24000 and 48000
169   // (kHz)) at initialization.
170   EXPECT_TRUE(TestOpusResampler(8000) == 8000);
171   EXPECT_TRUE(TestOpusResampler(12000) == 12000);
172   EXPECT_TRUE(TestOpusResampler(16000) == 16000);
173   EXPECT_TRUE(TestOpusResampler(24000) == 24000);
174   EXPECT_TRUE(TestOpusResampler(48000) == 48000);
175 
176   // Otherwise, it should be resampled to 48kHz by resampler.
177   EXPECT_TRUE(TestOpusResampler(9600) == 48000);
178   EXPECT_TRUE(TestOpusResampler(44100) == 48000);
179 }
180 
TEST(OpusAudioTrackEncoder,FetchMetadata)181 TEST(OpusAudioTrackEncoder, FetchMetadata)
182 {
183   const int32_t channels = 1;
184   const TrackRate sampleRate = 44100;
185   MediaQueue<EncodedFrame> frames;
186   TestOpusTrackEncoder encoder(sampleRate, frames);
187   EXPECT_TRUE(encoder.TestOpusRawCreation(channels));
188 
189   RefPtr<TrackMetadataBase> metadata = encoder.GetMetadata();
190   ASSERT_EQ(TrackMetadataBase::METADATA_OPUS, metadata->GetKind());
191 
192   RefPtr<OpusMetadata> opusMeta = static_cast<OpusMetadata*>(metadata.get());
193   EXPECT_EQ(channels, opusMeta->mChannels);
194   EXPECT_EQ(sampleRate, opusMeta->mSamplingFrequency);
195 }
196 
TEST(OpusAudioTrackEncoder,FrameEncode)197 TEST(OpusAudioTrackEncoder, FrameEncode)
198 {
199   const int32_t channels = 1;
200   const TrackRate sampleRate = 44100;
201   MediaQueue<EncodedFrame> frames;
202   TestOpusTrackEncoder encoder(sampleRate, frames);
203   EXPECT_TRUE(encoder.TestOpusRawCreation(channels));
204 
205   // Generate five seconds of raw audio data.
206   AudioGenerator<AudioDataValue> generator(channels, sampleRate);
207   AudioSegment segment;
208   const int32_t samples = sampleRate * 5;
209   generator.Generate(segment, samples);
210 
211   encoder.AppendAudioSegment(std::move(segment));
212   encoder.NotifyEndOfStream();
213 
214   EXPECT_TRUE(encoder.IsEncodingComplete());
215   EXPECT_TRUE(frames.IsFinished());
216 
217   // Verify that encoded data is 5 seconds long.
218   uint64_t totalDuration = 0;
219   while (RefPtr<EncodedFrame> frame = frames.PopFront()) {
220     totalDuration += frame->mDuration;
221   }
222   // 44100 as used above gets resampled to 48000 for opus.
223   const uint64_t five = 48000 * 5;
224   EXPECT_EQ(five + encoder.GetLookahead(), totalDuration);
225 }
226 
TEST(OpusAudioTrackEncoder,DefaultInitDuration)227 TEST(OpusAudioTrackEncoder, DefaultInitDuration)
228 {
229   const TrackRate rate = 44100;
230   MediaQueue<EncodedFrame> frames;
231   OpusTrackEncoder encoder(rate, frames);
232   AudioGenerator<AudioDataValue> generator(2, rate);
233   AudioSegment segment;
234   // 15 seconds should trigger the default-init rate.
235   // The default-init timeout is evaluated once per chunk, so keep chunks
236   // reasonably short.
237   for (int i = 0; i < 150; ++i) {
238     generator.Generate(segment, rate / 10);
239   }
240   encoder.AppendAudioSegment(std::move(segment));
241   encoder.NotifyEndOfStream();
242 
243   EXPECT_TRUE(encoder.IsEncodingComplete());
244   EXPECT_TRUE(frames.IsFinished());
245 
246   // Verify that encoded data is 15 seconds long.
247   uint64_t totalDuration = 0;
248   while (RefPtr<EncodedFrame> frame = frames.PopFront()) {
249     totalDuration += frame->mDuration;
250   }
251   // 44100 as used above gets resampled to 48000 for opus.
252   const uint64_t fifteen = 48000 * 15;
253   EXPECT_EQ(totalDuration, fifteen + encoder.GetLookahead());
254 }
255 
TestSampleRate(TrackRate aSampleRate,uint64_t aInputFrames)256 uint64_t TestSampleRate(TrackRate aSampleRate, uint64_t aInputFrames) {
257   MediaQueue<EncodedFrame> frames;
258   OpusTrackEncoder encoder(aSampleRate, frames);
259   AudioGenerator<AudioDataValue> generator(2, aSampleRate);
260   AudioSegment segment;
261   const uint64_t chunkSize = aSampleRate / 10;
262   const uint64_t chunks = aInputFrames / chunkSize;
263   // 15 seconds should trigger the default-init rate.
264   // The default-init timeout is evaluated once per chunk, so keep chunks
265   // reasonably short.
266   for (size_t i = 0; i < chunks; ++i) {
267     generator.Generate(segment, chunkSize);
268   }
269   generator.Generate(segment, aInputFrames % chunks);
270   encoder.AppendAudioSegment(std::move(segment));
271   encoder.NotifyEndOfStream();
272 
273   EXPECT_TRUE(encoder.IsEncodingComplete());
274   EXPECT_TRUE(frames.IsFinished());
275 
276   // Verify that encoded data is 15 seconds long.
277   uint64_t totalDuration = 0;
278   while (RefPtr<EncodedFrame> frame = frames.PopFront()) {
279     totalDuration += frame->mDuration;
280   }
281   return totalDuration - encoder.GetLookahead();
282 }
283 
TEST(OpusAudioTrackEncoder,DurationSampleRates)284 TEST(OpusAudioTrackEncoder, DurationSampleRates)
285 {
286   // Factors of 48k
287   EXPECT_EQ(TestSampleRate(48000, 48000 * 3 / 2), 48000U * 3 / 2);
288   EXPECT_EQ(TestSampleRate(24000, 24000 * 3 / 2), 48000U * 3 / 2);
289   EXPECT_EQ(TestSampleRate(16000, 16000 * 3 / 2), 48000U * 3 / 2);
290   EXPECT_EQ(TestSampleRate(12000, 12000 * 3 / 2), 48000U * 3 / 2);
291   EXPECT_EQ(TestSampleRate(8000, 8000 * 3 / 2), 48000U * 3 / 2);
292 
293   // Non-factors of 48k, resampled
294   EXPECT_EQ(TestSampleRate(44100, 44100 * 3 / 2), 48000U * 3 / 2);
295   EXPECT_EQ(TestSampleRate(32000, 32000 * 3 / 2), 48000U * 3 / 2);
296   EXPECT_EQ(TestSampleRate(96000, 96000 * 3 / 2), 48000U * 3 / 2);
297   EXPECT_EQ(TestSampleRate(33330, 33330 * 3 / 2), 48000U * 3 / 2);
298 }
299