/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <stdint.h>
#include <math.h>
#include <memory>
#include "../AudioPacketizer.h"
#include "../TimedPacketizer.h"
#include "gtest/gtest.h"
using namespace mozilla;
template <
typename T>
class AutoBuffer {
public:
explicit AutoBuffer(size_t aLength) { mStorage =
new T[aLength]; }
~AutoBuffer() {
delete[] mStorage; }
T* Get() {
return mStorage; }
private:
T* mStorage;
};
int16_t Sequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart =
0) {
uint32_t i;
for (i =
0; i < aSize; i++) {
aBuffer[i] = (aStart + i) % INT16_MAX;
}
return aStart + i;
}
void IsSequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart =
0) {
for (uint32_t i =
0; i < aSize; i++) {
ASSERT_EQ(aBuffer[i],
static_cast<int64_t>((aStart + i) % INT16_MAX))
<<
"Buffer is not a sequence at offset " << i <<
'\n';
}
// Buffer is a sequence.
}
void Zero(std::unique_ptr<int16_t[]> aBuffer, uint32_t aSize) {
for (uint32_t i =
0; i < aSize; i++) {
ASSERT_TRUE(aBuffer[i] ==
0)
<<
"Buffer is not null at offset " << i <<
'\n';
}
}
double sine(uint32_t aPhase) {
return sin(aPhase *
2 * M_PI *
440 /
44100); }
TEST(AudioPacketizer, Test)
{
for (int16_t channels =
1; channels <
2; channels++) {
// Test that the packetizer returns zero on underrun
{
AudioPacketizer<int16_t, int16_t> ap(
441, channels);
for (int16_t i =
0; i <
10; i++) {
std::unique_ptr<int16_t[]> out(ap.Output());
Zero(std::move(out),
441);
}
}
// Simple test, with input/output buffer size aligned on the packet size,
// alternating Input and Output calls.
{
AudioPacketizer<int16_t, int16_t> ap(
441, channels);
int16_t seqEnd =
0;
for (int16_t i =
0; i <
10; i++) {
AutoBuffer<int16_t> b(
441 * channels);
int16_t prevEnd = seqEnd;
seqEnd = Sequence(b.Get(), channels *
441, prevEnd);
ap.Input(b.Get(),
441);
std::unique_ptr<int16_t[]> out(ap.Output());
IsSequence(out.get(),
441 * channels, prevEnd);
}
}
// Simple test, with input/output buffer size aligned on the packet size,
// alternating two Input and Output calls.
{
AudioPacketizer<int16_t, int16_t> ap(
441, channels);
int16_t seqEnd =
0;
for (int16_t i =
0; i <
10; i++) {
AutoBuffer<int16_t> b(
441 * channels);
AutoBuffer<int16_t> b1(
441 * channels);
int16_t prevEnd0 = seqEnd;
seqEnd = Sequence(b.Get(),
441 * channels, prevEnd0);
int16_t prevEnd1 = seqEnd;
seqEnd = Sequence(b1.Get(),
441 * channels, seqEnd);
ap.Input(b.Get(),
441);
ap.Input(b1.Get(),
441);
std::unique_ptr<int16_t[]> out(ap.Output());
std::unique_ptr<int16_t[]> out2(ap.Output());
IsSequence(out.get(),
441 * channels, prevEnd0);
IsSequence(out2.get(),
441 * channels, prevEnd1);
}
}
// Input/output buffer size not aligned on the packet size,
// alternating two Input and Output calls.
{
AudioPacketizer<int16_t, int16_t> ap(
441, channels);
int16_t prevEnd =
0;
int16_t prevSeq =
0;
for (int16_t i =
0; i <
10; i++) {
AutoBuffer<int16_t> b(
480 * channels);
AutoBuffer<int16_t> b1(
480 * channels);
prevSeq = Sequence(b.Get(),
480 * channels, prevSeq);
prevSeq = Sequence(b1.Get(),
480 * channels, prevSeq);
ap.Input(b.Get(),
480);
ap.Input(b1.Get(),
480);
std::unique_ptr<int16_t[]> out(ap.Output());
std::unique_ptr<int16_t[]> out2(ap.Output());
IsSequence(out.get(),
441 * channels, prevEnd);
prevEnd +=
441 * channels;
IsSequence(out2.get(),
441 * channels, prevEnd);
prevEnd +=
441 * channels;
}
printf(
"Available: %d\n", ap.PacketsAvailable());
}
// "Real-life" test case: streaming a sine wave through a packetizer, and
// checking that we have the right output.
// 128 is, for example, the size of a Web Audio API block, and 441 is the
// size of a webrtc.org packet when the sample rate is 44100 (10ms)
{
AudioPacketizer<int16_t, int16_t> ap(
441, channels);
AutoBuffer<int16_t> b(
128 * channels);
uint32_t phase =
0;
uint32_t outPhase =
0;
for (int16_t i =
0; i <
1000; i++) {
for (int32_t j =
0; j <
128; j++) {
for (int32_t c =
0; c < channels; c++) {
// int16_t sinewave at 440Hz/44100Hz sample rate
b.Get()[j * channels + c] = (
2 <<
14) * sine(phase);
}
phase++;
}
ap.Input(b.Get(),
128);
while (ap.PacketsAvailable()) {
std::unique_ptr<int16_t[]> packet(ap.Output());
for (uint32_t k =
0; k < ap.mPacketSize; k++) {
for (int32_t c =
0; c < channels; c++) {
ASSERT_TRUE(packet[k * channels + c] ==
static_cast<int16_t>(((
2 <<
14) * sine(outPhase))));
}
outPhase++;
}
}
}
}
// Test that clearing the packetizer empties it and starts returning zeros.
{
AudioPacketizer<int16_t, int16_t> ap(
441, channels);
AutoBuffer<int16_t> b(
440 * channels);
Sequence(b.Get(),
440 * channels);
ap.Input(b.Get(),
440);
EXPECT_EQ(ap.FramesAvailable(),
440U);
ap.Clear();
EXPECT_EQ(ap.FramesAvailable(),
0U);
EXPECT_TRUE(ap.Empty());
std::unique_ptr<int16_t[]> out(ap.Output());
Zero(std::move(out),
441);
}
}
}
TEST(TimedPacketizer, Test)
{
const int channels =
2;
const int64_t rate =
48000;
const int64_t inputPacketSize =
240;
const int64_t packetSize =
96;
TimedPacketizer<int16_t, int16_t> tp(packetSize, channels,
0, rate);
int16_t prevEnd =
0;
int16_t prevSeq =
0;
nsTArray<int16_t> packet;
uint64_t tsCheck =
0;
packet.SetLength(tp.PacketSize() * channels);
for (int16_t i =
0; i <
10; i++) {
AutoBuffer<int16_t> b(inputPacketSize * channels);
prevSeq = Sequence(b.Get(), inputPacketSize * channels, prevSeq);
tp.Input(b.Get(), inputPacketSize);
while (tp.PacketsAvailable()) {
media::TimeUnit ts = tp.Output(packet.Elements());
IsSequence(packet.Elements(), packetSize * channels, prevEnd);
EXPECT_EQ(ts, media::TimeUnit(tsCheck, rate));
prevEnd += packetSize * channels;
tsCheck += packetSize;
}
}
EXPECT_TRUE(!tp.PacketsAvailable());
uint32_t drained;
media::TimeUnit ts = tp.Drain(packet.Elements(), drained);
EXPECT_EQ(ts, media::TimeUnit(tsCheck, rate));
EXPECT_LE(drained, packetSize);
}