Browse Source

Exercise call clocks through Opus and jitter recovery

master
evgeny 4 days ago
parent
commit
a978bb7b06
  1. 53
      tools/chatgui/tests/test_call_jitter.cpp
  2. 25
      tools/chatgui/tests/test_call_timeline.cpp

53
tools/chatgui/tests/test_call_jitter.cpp

@ -28,6 +28,58 @@ static int decode_bad(void* arg, const uint8_t*, int, int16_t*, int) {
static void push(call_jitter* j, int id, int64_t received_ms = -1) {
call_jitter_push(j, (const uint8_t*)&id, sizeof(id), (uint32_t)(id * 20), received_ms < 0 ? id * 20 : received_ms, nullptr);
}
static void test_audio_clock() {
const uint64_t id = 987;
CHECK(call_audio_start(id) == 0);
auto* encoder = opus_codec_encoder_create(48000, 1);
CHECK(encoder);
int delay = 0; CHECK(opus_codec_encoder_delay(encoder, &delay) == 0);
int16_t voice[960], output[480]; uint8_t packet[512];
int sent = 0, valid = 0;
uint64_t written = 0;
int64_t previous = -1;
call_clock_snapshot clock{};
CHECK(call_audio_get_clock(id, 0, &clock) == 0 && !clock.valid);
uint64_t epoch = clock.epoch;
// Начальный burst заставляет настоящий jitter менять темп; PTS не зависит от send_ts.
for (int tick = 0; tick < 4000; tick++) {
int count = tick == 0 ? 30 : (tick % 2 == 0 ? 1 : 0);
for (int i = 0; i < count; i++, sent++) {
for (int s = 0; s < 960; s++) voice[s] = (int16_t)(8000 * std::sin((sent * 960ll + s) * 0.09));
int len = opus_codec_encode(encoder, voice, 960, packet, sizeof(packet)); CHECK(len > 0);
call_capture_time timing{1000000 + sent * 20000ll, 9, (uint32_t)delay};
call_audio_on_media(nullptr, id, (uint16_t)sent, 0, packet, len, &timing);
}
CHECK(call_audio_pull_pcm(id, output, 480) == 480); written += 480;
int64_t now = tick * 10000ll;
call_audio_playback_progress(id, epoch, written, now, now, 1);
CHECK(call_audio_get_clock(id, now, &clock) == 0 && clock.written_samples == written);
if (clock.valid) {
valid++;
CHECK(clock.source_epoch == 9 && clock.pts_us >= previous);
CHECK(clock.pts_us <= 1000000 + sent * 20000ll);
CHECK(clock.pts_us > 1000000 + sent * 20000ll - 700000);
previous = clock.pts_us;
}
}
CHECK(valid > 3900);
// Underrun и местные тоны продолжают device-position, но удерживают source-PTS.
int64_t held = -1;
for (int tick = 4000; tick < 4300; tick++) {
CHECK(call_audio_pull_pcm(id, output, 480) == 480); written += 480;
call_audio_playback_progress(id, epoch, written, tick * 10000ll, tick * 10000ll, 1);
CHECK(call_audio_get_clock(id, tick * 10000ll, &clock) == 0 && clock.valid);
if (tick == 4200) held = clock.pts_us;
if (tick > 4200) CHECK(clock.pts_us == held);
}
call_audio_reset_io(id, 1);
CHECK(call_audio_get_clock(id, 43000000, &clock) == 0 && clock.epoch != epoch && !clock.valid);
call_audio_playback_progress(id, epoch, written, 43000000, 43000000, 1);
CHECK(call_audio_get_clock(id, 43000000, &clock) == 0 && !clock.valid);
call_audio_stop(); opus_codec_encoder_destroy(encoder);
std::printf("audio clock: real Opus, burst/tempo, device progress, local tones and reset checked\n");
}
int main() {
{
call_jitter_window w;
@ -58,6 +110,7 @@ int main() {
}
debug_set_level(DEBUG_LEVEL_WARN);
debug_enable_console(1);
test_audio_clock();
int16_t pcm[480];
int depth, tempo;
uint32_t dropped, under;

25
tools/chatgui/tests/test_call_timeline.cpp

@ -83,8 +83,31 @@ static void test_long_run() {
DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS timeline: 200s actual SoundTouch, tempo jumps, bounded rings, monotonic clock");
}
static void test_failure_and_recovery() {
CallTimeline map;
auto start = [&] {
map.reset(5); map.feed(1000000, 960, 1);
map.source({SourceKind::Copy, SourceError::None, 0, 0, 0, 960}); map.read(960);
map.progress(5, 480, 10000, 10000, true);
CHECK(map.clock(10000).valid);
};
start(); map.progress(5, 240, 20000, 20000, true);
CHECK(!map.clock(20000).valid);
map.progress(5, 960, 30000, 30000, true); CHECK(!map.clock(30000).valid);
start(); map.progress(5, 961, 20000, 20000, true); CHECK(!map.clock(20000).valid);
start(); map.read(1); CHECK(!map.clock(10000).valid);
start(); // Переполнение карты без device-progress не портит звук и не даёт ложный PTS.
for (unsigned i = 1; i < 2200; i++) {
map.feed(1000000 + i * 20000ll, 960, 1);
map.source({SourceKind::Copy, SourceError::None, i * 960, i * 960, 0, 960}); map.read(960);
}
CHECK(!map.clock(10000).valid);
start();
DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS timeline: regressing/impossible device head, overread, bounded overflow, reset recovery");
}
int main() {
debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO);
test_boundaries(); test_mix(); test_long_run();
test_boundaries(); test_mix(); test_long_run(); test_failure_and_recovery();
return 0;
}

Loading…
Cancel
Save