152 lines
6.8 KiB
C++
152 lines
6.8 KiB
C++
// Сохранность FIFO, десятисекундный burst, догон при продолжающемся входе и повторное накопление.
|
|
#include "call/call_jitter.h"
|
|
#include "call/call_jitter_window.h"
|
|
#include "call/call_audio.h"
|
|
#include "../../../lib/opus_codec.h"
|
|
#include "../../../lib/debug_config.h"
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
static int failures;
|
|
#define CHECK(c) do { if (!(c)) { std::printf("FAIL line %d: %s\n", __LINE__, #c); failures++; } } while (0)
|
|
struct decoder { std::vector<int> seen; };
|
|
static int decode(void* arg, const uint8_t* enc, int len, int16_t* pcm, int cap) {
|
|
auto* d = static_cast<decoder*>(arg);
|
|
int id = -1;
|
|
if (len == sizeof(id)) std::memcpy(&id, enc, sizeof(id));
|
|
d->seen.push_back(id);
|
|
for (int i = 0; i < cap; i++) pcm[i] = (int16_t)(8000 * std::sin(2 * 3.141592653589793 * 440 * i / 48000));
|
|
return cap;
|
|
}
|
|
static void push(call_jitter* j, int id) { call_jitter_push(j, (const uint8_t*)&id, sizeof(id)); }
|
|
int main() {
|
|
{
|
|
call_jitter_window w;
|
|
w.observe(0, 100); w.observe(500, 400);
|
|
CHECK(w.low == 100 && w.high == 400 && w.reserve == 300);
|
|
w.observe(1000, 2000); CHECK(w.reserve == 1000);
|
|
w.observe(10000, 2000); CHECK(w.low == 2000 && w.reserve == 60);
|
|
w.observe(30000, 80); CHECK(w.low == 80 && w.high == 80 && w.reserve == 60);
|
|
for (int cap : {500, 2000}) {
|
|
call_jitter_window limited;
|
|
limited.max_reserve = cap;
|
|
limited.observe(0, 0); limited.observe(500, 5000);
|
|
CHECK(limited.reserve == cap);
|
|
}
|
|
}
|
|
debug_set_level(DEBUG_LEVEL_WARN);
|
|
int16_t pcm[480];
|
|
int depth, tempo;
|
|
uint32_t dropped, under;
|
|
{
|
|
decoder d;
|
|
auto* j = call_jitter_create(decode, &d, 1000);
|
|
CHECK(j);
|
|
push(j, 0); push(j, 1);
|
|
CHECK(call_jitter_pull(j, pcm, 480) == 0);
|
|
push(j, 2);
|
|
CHECK(call_jitter_pull(j, pcm, 480) > 0);
|
|
for (int i = 3; i < 503; i++) push(j, i); // 10с накопления, без удаления существовавшего хвоста
|
|
int next = 503, highest = 100;
|
|
for (int tick = 0; tick < 6000; tick++) {
|
|
if (tick % 2 == 0) push(j, next++); // непрерывный live: кадр каждые 20мс
|
|
call_jitter_pull(j, pcm, 480);
|
|
call_jitter_get_stats(j, &depth, &tempo, &dropped, &under, nullptr);
|
|
if (tempo > highest) highest = tempo;
|
|
}
|
|
call_jitter_range range;
|
|
call_jitter_get_stats(j, nullptr, nullptr, nullptr, nullptr, &range);
|
|
CHECK(range.reserve_ms == 1000 && range.max_ms >= 10000);
|
|
CHECK(range.min_ms <= range.max_ms);
|
|
CHECK(highest >= 150);
|
|
CHECK(depth < 1200);
|
|
CHECK(tempo <= 110);
|
|
CHECK(dropped == 0);
|
|
for (size_t i = 0; i < d.seen.size(); i++) CHECK(d.seen[i] == (int)i);
|
|
for (int i = 0; i < 100; i++) call_jitter_pull(j, pcm, 480);
|
|
size_t before = d.seen.size();
|
|
push(j, next++); push(j, next++);
|
|
CHECK(call_jitter_pull(j, pcm, 480) == 0);
|
|
CHECK(d.seen.size() == before);
|
|
for (int i = 0; i < 50; i++) push(j, next++);
|
|
CHECK(call_jitter_pull(j, pcm, 480) > 0);
|
|
call_jitter_destroy(j);
|
|
}
|
|
{
|
|
decoder d;
|
|
auto* j = call_jitter_create(decode, &d, 1000);
|
|
for (int i = 0; i < CALL_JITTER_MAX_FRAMES; i++) push(j, i);
|
|
call_jitter_get_stats(j, &depth, nullptr, &dropped, nullptr, nullptr);
|
|
CHECK(depth == 15000 && dropped == 0);
|
|
push(j, CALL_JITTER_MAX_FRAMES);
|
|
call_jitter_get_stats(j, &depth, nullptr, &dropped, nullptr, nullptr);
|
|
CHECK(depth == 15000 && dropped == 1);
|
|
CHECK(call_jitter_pull(j, pcm, 480) > 0);
|
|
CHECK(d.seen.front() == 1);
|
|
call_jitter_destroy(j);
|
|
}
|
|
for (int samples : {240, 480, 960}) {
|
|
decoder d;
|
|
auto* j = call_jitter_create(decode, &d, 1000);
|
|
for (int i = 0; i < 500; i++) push(j, i);
|
|
std::vector<int16_t> output(samples);
|
|
int elapsed = 0, previous = 100;
|
|
for (; elapsed < 500; ) {
|
|
CHECK(call_jitter_pull(j, output.data(), samples) == samples);
|
|
elapsed += samples * 1000 / 48000;
|
|
call_jitter_get_stats(j, nullptr, &tempo, nullptr, nullptr, nullptr);
|
|
int expected = (int)std::lround(100 + 60.0 * elapsed / 500);
|
|
CHECK(std::abs(tempo - expected) <= 1);
|
|
previous = tempo;
|
|
}
|
|
CHECK(tempo == 160);
|
|
bool slowing = false;
|
|
for (int tick = 0; tick < 3000 * 960 / samples; tick++) {
|
|
call_jitter_pull(j, output.data(), samples);
|
|
call_jitter_get_stats(j, &depth, &tempo, &dropped, nullptr, nullptr);
|
|
int max_step = (int)std::ceil(120.0 * samples / 48000);
|
|
CHECK(std::abs(tempo - previous) <= max_step);
|
|
if (tempo < previous) slowing = true;
|
|
previous = tempo;
|
|
}
|
|
CHECK(slowing && tempo <= 110 && dropped == 0);
|
|
call_jitter_destroy(j);
|
|
}
|
|
{
|
|
const uint64_t id = 123;
|
|
CHECK(call_audio_start(id) == 0);
|
|
auto* encoder = opus_codec_encoder_create(48000, 1);
|
|
CHECK(encoder);
|
|
int16_t voice[960];
|
|
for (int i = 0; i < 960; i++) voice[i] = (int16_t)(8000 * std::sin(2 * 3.141592653589793 * 440 * i / 48000));
|
|
uint8_t encoded[512];
|
|
int len = opus_codec_encode(encoder, voice, 960, encoded, sizeof(encoded));
|
|
CHECK(len > 0);
|
|
for (int i = 0; i < 3; i++) call_audio_on_media(nullptr, id, i, 0, encoded, len);
|
|
for (int i = 0; i < 30; i++) call_audio_pull_pcm(id, pcm, 480);
|
|
// Аудиоустройства и uasync в тесте нет: проверяем реальный срок включения сигнала.
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
call_audio_pull_pcm(id, pcm, 480);
|
|
bool silent = true;
|
|
for (auto value : pcm) if (value) silent = false;
|
|
CHECK(silent);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
call_audio_pull_pcm(id, pcm, 480);
|
|
silent = true;
|
|
for (auto value : pcm) if (value) silent = false;
|
|
CHECK(!silent);
|
|
for (int i = 0; i < 6; i++) call_audio_on_media(nullptr, id, i+3, 0, encoded, len);
|
|
CHECK(call_audio_pull_pcm(id, pcm, 480) == 480);
|
|
call_audio_begin_end(id); // явное завершение прекращает догон и включает финальный тон
|
|
CHECK(call_audio_pull_pcm(id, pcm, 480) > 0);
|
|
call_audio_release(id);
|
|
opus_codec_encoder_destroy(encoder);
|
|
}
|
|
std::printf("call_jitter: %s\n", failures ? "FAILED" : "PASSED");
|
|
return failures ? 1 : 0;
|
|
}
|