call: continue bounded video flush without waiting for media ticks
This commit is contained in:
1 parent
ff51e30a55
commit
9886bc2fde
3 files changed
+167
-9
No files matched your search
@@ -13,6 +13,7 @@ check_PROGRAMS = \
|
||||
test_call_video_rate \
|
||||
test_call_video_playout \
|
||||
test_call_video \
|
||||
test_call_video_flush \
|
||||
test_memory_pool_and_config \
|
||||
test_serialize \
|
||||
test_reed_solomon \
|
||||
@@ -688,6 +689,9 @@ test_call_video_playout_LDADD = $(LIBUTUN) $(COMMON_LIBS)
|
||||
|
||||
test_call_video_SOURCES = test_call_video.c
|
||||
test_call_video_LDADD = $(LIBUTUN) $(COMMON_LIBS)
|
||||
|
||||
test_call_video_flush_SOURCES = test_call_video_flush.c
|
||||
test_call_video_flush_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
test_call_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/src/dm -I$(top_srcdir)/src/call -I$(top_srcdir)/src/routing_layer -I$(top_srcdir)/src/transport_layer -I$(top_srcdir)/lib
|
||||
test_call_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/* Настоящие transport/fragment/uasync, управляемый router без сетевых таймеров. */
|
||||
#define etcp_router_conn_get test_router_conn_get
|
||||
#define etcp_router_conn_send test_router_conn_send
|
||||
#define etcp_router_tx_stats test_router_tx_stats
|
||||
#define etcp_router_send_q_has_room test_router_send_q_has_room
|
||||
#define etcp_router_resume_receive test_router_resume_receive
|
||||
#include "../src/call/call_video_transport.c"
|
||||
#undef etcp_router_conn_get
|
||||
#undef etcp_router_conn_send
|
||||
#undef etcp_router_tx_stats
|
||||
#undef etcp_router_send_q_has_room
|
||||
#undef etcp_router_resume_receive
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Один batch, без poll/timeouts: тест не может случайно продолжить TX таймером 50мс. */
|
||||
extern void process_immediate_queue(struct UASYNC *ua);
|
||||
|
||||
static struct ETCP_ROUTER_CONN router;
|
||||
static unsigned packets, received, room, resumed;
|
||||
static uint64_t debt;
|
||||
enum { FRAME_BYTES = 45001 };
|
||||
static uint8_t output[FRAME_BYTES];
|
||||
|
||||
struct ETCP_ROUTER_CONN *test_router_conn_get(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, uint8_t svc) {
|
||||
(void)inst; (void)group; (void)peer; assert(svc == ETCP_RT_ID_CALL_VIDEO); return &router;
|
||||
}
|
||||
int test_router_conn_send(struct ETCP_ROUTER_CONN *conn, const uint8_t *data, size_t len, int mode) {
|
||||
assert(conn == &router && !mode);
|
||||
struct call_video_frame f;
|
||||
assert(!call_video_fragment_parse(data, len, &f));
|
||||
assert(f.call_id == 7 && f.frame_id == 1 && f.size == FRAME_BYTES && f.offset == received);
|
||||
assert(received + f.length <= sizeof(output));
|
||||
memcpy(output + received, f.data, f.length); received += f.length; packets++; return 0;
|
||||
}
|
||||
int test_router_tx_stats(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, uint8_t svc,
|
||||
uint64_t now_us, struct ETCP_ROUTER_TX_STATS *out) {
|
||||
(void)inst; (void)group; (void)peer; (void)svc; (void)now_us;
|
||||
*out = (struct ETCP_ROUTER_TX_STATS){.inflight_bytes = debt, .ready = 1}; return 0;
|
||||
}
|
||||
int test_router_send_q_has_room(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, uint8_t svc) {
|
||||
(void)inst; (void)group; (void)peer; (void)svc; return room;
|
||||
}
|
||||
void test_router_resume_receive(struct UTUN_INSTANCE *inst, uint64_t group, uint64_t peer, uint8_t svc) {
|
||||
(void)inst; (void)group; (void)peer; (void)svc; resumed++;
|
||||
}
|
||||
|
||||
static struct call_video_transport *setup(struct call_session *s) {
|
||||
struct call_video_transport *v = u_calloc(1, sizeof(*v)); assert(v);
|
||||
v->session = s; s->video = v;
|
||||
v->receiver = call_video_receiver_create(); assert(v->receiver);
|
||||
v->have_peer = 1; v->local_revision = v->acknowledged_revision = 1;
|
||||
v->local.encode[0] = (struct call_video_limit){1280, 720, 30, 1280 * 720 * 30, 4000000, CALL_VIDEO_BASELINE, 31};
|
||||
v->peer.decode[0] = v->local.encode[0];
|
||||
v->audio_end_pts = 1000000;
|
||||
v->tx_count = 1; v->tx_bytes = FRAME_BYTES;
|
||||
struct video_tx_frame *head = &v->tx[0];
|
||||
head->data = u_malloc(FRAME_BYTES); assert(head->data);
|
||||
for (unsigned i = 0; i < FRAME_BYTES; i++) head->data[i] = (uint8_t)(i * 17 + i / 257);
|
||||
head->started_tb = get_time_tb();
|
||||
head->frame = (struct call_video_frame){.call_id = 7, .frame_id = 1, .pts_us = 1000000,
|
||||
.format = {1, 640, 480, 30, 0, CALL_VIDEO_H264, 31, 4000000, CALL_VIDEO_BASELINE}, .size = FRAME_BYTES};
|
||||
packets = received = resumed = 0; room = 1; debt = 0;
|
||||
memset(output, 0, sizeof(output)); return v;
|
||||
}
|
||||
static void check_complete(struct call_video_transport *v) {
|
||||
assert(packets == 38 && received == FRAME_BYTES && !v->tx_count && !v->tx_bytes && !v->flush_pending);
|
||||
for (unsigned i = 0; i < FRAME_BYTES; i++) assert(output[i] == (uint8_t)(i * 17 + i / 257));
|
||||
}
|
||||
static void cleanup(struct call_session *s) {
|
||||
call_video_transport_stop(s); assert(!s->video && resumed == 1);
|
||||
unsigned before = packets;
|
||||
uasync_drain_immediate(s->ctx->inst->ua);
|
||||
assert(packets == before);
|
||||
assert(s->ctx->inst->ua->timer_alloc_count == s->ctx->inst->ua->timer_free_count);
|
||||
}
|
||||
int main(void) {
|
||||
debug_set_level(DEBUG_LEVEL_WARN);
|
||||
struct UASYNC *ua = uasync_create(); assert(ua);
|
||||
struct UTUN_INSTANCE inst = {.ua = ua};
|
||||
struct call_ctx ctx = {.inst = &inst};
|
||||
struct call_session s = {.ctx = &ctx, .call_id = 7};
|
||||
router.send_q = queue_new(ua, 0, 0, 0, "video_flush_test"); assert(router.send_q);
|
||||
|
||||
struct call_video_transport *v = setup(&s);
|
||||
flush(v); assert(packets == 16 && v->flush_pending);
|
||||
process_immediate_queue(ua); assert(packets == 32 && v->flush_pending);
|
||||
process_immediate_queue(ua); check_complete(v); cleanup(&s);
|
||||
puts("PASS: 45001-byte frame completes in bounded batches without audio or periodic timers; bytes preserved");
|
||||
|
||||
for (unsigned gate = 0; gate < 2; gate++) {
|
||||
v = setup(&s); flush(v);
|
||||
if (gate) debt = 1000000; else room = 0;
|
||||
process_immediate_queue(ua); assert(packets == 16 && !v->flush_pending && v->flush_net_wait == 1);
|
||||
process_immediate_queue(ua); assert(packets == 16);
|
||||
room = 1; debt = 0; flush(v); uasync_drain_immediate(ua); check_complete(v); cleanup(&s);
|
||||
}
|
||||
puts("PASS: router room and inflight debt stop continuation without spinning; TX resumes exactly once");
|
||||
|
||||
v = setup(&s); v->audio_end_pts--;
|
||||
flush(v); assert(!packets && !v->flush_pending);
|
||||
call_video_audio_sent(&s, 1000000); uasync_drain_immediate(ua); check_complete(v); cleanup(&s);
|
||||
v = setup(&s); v->acknowledged_revision = 0;
|
||||
flush(v); assert(!packets && !v->flush_pending);
|
||||
v->acknowledged_revision = 1; flush(v); uasync_drain_immediate(ua); check_complete(v); cleanup(&s);
|
||||
puts("PASS: audio position and CAPS acknowledgement remain required");
|
||||
|
||||
v = setup(&s); flush(v);
|
||||
call_video_audio_sent(&s, 1020000); assert(packets == 32);
|
||||
process_immediate_queue(ua); check_complete(v); cleanup(&s);
|
||||
puts("PASS: audio flush supersedes pending continuation without duplicate fragments");
|
||||
|
||||
v = setup(&s); flush(v); cleanup(&s); assert(packets == 16);
|
||||
v = setup(&s); flush(v); discard_tx(v, "test_detach");
|
||||
assert(!v->tx_count && !v->flush_pending && v->need_key);
|
||||
uasync_drain_immediate(ua); assert(packets == 16); cleanup(&s);
|
||||
puts("PASS: stop and discard cancel pending callbacks before freeing their owner/data");
|
||||
queue_free(router.send_q); uasync_destroy(ua, 0); return 0;
|
||||
}
|
||||
Reference in new issue
Block a user