Files
utun2/tests/test_call_audio_batch.c
T

108 lines
5.9 KiB
C

/* Wire: границы размера пачек, полный разбор и атомарный отказ. */
#include "../src/call/call_audio_batch.h"
#include "../src/routing_layer/etcp_router.h"
#include "../lib/debug_config.h"
#include <stdlib.h>
#include <string.h>
#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "audio batch test line=%d: %s", __LINE__, #x); exit(1); } } while (0)
static void wire_test(void) {
uint8_t opus[57], wire[CALL_MEDIA_MAX_BYTES], saved[CALL_MEDIA_MAX_BYTES];
memset(opus, 0xa5, sizeof(opus));
struct call_audio_batch_view b = {.header = {.subcmd = CALL_SUBCMD_MEDIA, .call_id = 1, .track_id = CALL_TRACK_AUDIO,
.capture_epoch = 17, .codec_delay_samples = 312, .sent_ms = UINT32_MAX}};
size_t length = 0;
for (unsigned n = 1; n <= 10; n++) {
b.header.count = n;
b.frames[n - 1] = (struct call_audio_frame_view){.header = {(uint16_t)(65532 + n), 1000000 + n * 20000, 37}, .opus = opus};
CHECK(call_audio_batch_encode(&b, wire, sizeof(wire), &length) == 0 && length == 27 + n * 49);
struct call_audio_batch_view out;
CHECK(call_audio_batch_parse(wire, length, &out) == 0 && out.header.count == n && out.header.sent_ms == UINT32_MAX);
for (unsigned i = 0; i < n; i++) CHECK(out.frames[i].header.seq == b.frames[i].header.seq
&& out.frames[i].header.capture_pts_us == b.frames[i].header.capture_pts_us
&& out.frames[i].header.length == 37 && !memcmp(out.frames[i].opus, opus, 37));
}
struct call_audio_batch_view out, before;
memset(&out, 0x77, sizeof(out)); before = out;
for (size_t n = 0; n < length; n++) {
CHECK(call_audio_batch_parse(wire, n, &out) == -1 && !memcmp(&out, &before, sizeof(out)));
}
CHECK(call_audio_batch_parse(wire, length + 1, &out) == -1);
memcpy(saved, wire, sizeof(wire));
wire[CALL_MEDIA_HDR_SIZE - 1] = 11;
CHECK(call_audio_batch_parse(wire, length, &out) == -1);
memcpy(wire, saved, sizeof(wire));
struct call_pkt_audio_frame invalid = {1, 0, UINT16_MAX};
memcpy(wire + CALL_MEDIA_HDR_SIZE, &invalid, sizeof(invalid));
CHECK(call_audio_batch_parse(wire, length, &out) == -1);
memset(wire, 0x55, sizeof(wire)); memcpy(saved, wire, sizeof(wire));
CHECK(call_audio_batch_encode(&b, wire, length - 1, &length) == -1 && !memcmp(wire, saved, sizeof(wire)));
b.header.count = 1; b.header.capture_epoch = 0;
CHECK(call_audio_batch_encode(&b, wire, sizeof(wire), &length) == -1);
b.frames[0].header.capture_pts_us = INT64_MIN;
CHECK(call_audio_batch_encode(&b, wire, sizeof(wire), &length) == 0);
CHECK(call_audio_batch_parse(wire, length, &out) == 0 && out.frames[0].header.capture_pts_us == INT64_MIN);
}
static void step(struct call_audio_batch_control *c, struct ETCP_ROUTER_TX_STATS *n, uint64_t t) {
n->last_ack_us = t; ++n->acked_packets; CHECK(call_audio_batch_step(c, t, n) == 0);
}
static void control_test(void) {
struct call_audio_batch_control c;
struct ETCP_ROUTER_TX_STATS n = {.local_epoch = 1, .peer_epoch = 2, .ready = 1, .acked_packets = 1,
.delivery_rtt_us = 20000, .window = {.valid = 1}};
call_audio_batch_init(&c, 0);
CHECK(c.target == 4);
CHECK(call_audio_batch_step(&c, 0, NULL) == 0 && c.target == 4);
n.ready = 0; n.acked_packets = 0; n.window.valid = 0;
step(&c, &n, 50000); CHECK(c.target == 4);
n.ready = 1; n.acked_packets = 1;
step(&c, &n, 100000); CHECK(c.target == 4); // Первый ACK до полного окна статистики.
n.window.valid = 1;
for (unsigned t = 150000; t < 2000000; t += 50000) step(&c, &n, t);
CHECK(c.target == 4);
/* Изолированная задержка не включает упаковку. */
n.delivery_rtt_us = 150000; step(&c, &n, 2000000); step(&c, &n, 2050000);
n.delivery_rtt_us = 20000; step(&c, &n, 2100000); CHECK(c.target == 4);
/* Постоянный RTT без роста очередей не заставляет увеличивать пачку. */
n.delivery_rtt_us = 150000;
for (unsigned t = 2150000; t <= 2350000; t += 50000) step(&c, &n, t);
CHECK(c.target == 4);
n.queued_age_us = 150000; n.queued_packets = 2;
for (unsigned t = 2400000; t <= 2850000; t += 50000) step(&c, &n, t);
CHECK(c.target == 6);
uint64_t ack = n.acked_packets;
for (unsigned t = 2900000; t <= 3500000; t += 50000) {
n.last_ack_us = t; CHECK(call_audio_batch_step(&c, t, &n) == 0);
CHECK(c.target == 6 && n.acked_packets == ack); // Один снимок не вызывает несколько увеличений.
}
n.delivery_rtt_us = 300000;
for (unsigned t = 3550000; t <= 9000000; t += 50000) step(&c, &n, t);
CHECK(c.target == 10);
n.queued_age_us = 0; n.queued_packets = 0;
n.delivery_rtt_us = 10000; step(&c, &n, 9050000);
CHECK(c.target == 10);
for (unsigned t = 9100000; t <= 11050000; t += 50000) step(&c, &n, t);
CHECK(c.target == 10);
n.peer_epoch = 3; step(&c, &n, 11100000); CHECK(c.target == 4);
n.inflight_age_us = 150000;
for (unsigned t = 11150000; t <= 11350000; t += 50000) step(&c, &n, t);
CHECK(c.target == 5);
n.receive_busy = 1;
for (unsigned t = 11400000; t <= 13000000; t += 50000) step(&c, &n, t);
CHECK(c.target == 5);
n.receive_busy = 0; CHECK(call_audio_batch_step(&c, 20000000, &n) == 0 && c.target == 5);
CHECK(call_audio_batch_step(&c, 19999999, &n) == -1);
CHECK(call_audio_batch_step(&c, 20000001, NULL) == 0 && c.target == 4);
n.inflight_age_us = 0;
for (unsigned t = 20100000; t <= 37000000; t += 50000) step(&c, &n, t);
CHECK(c.target == 1);
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR);
wire_test(); control_test();
debug_set_level(DEBUG_LEVEL_INFO);
DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS audio batch: 1..10, partial/trailing/invalid wire, atomic output, adaptive target/hysteresis/epochs");
return 0;
}