/* Управляемое время: весь поток сохраняется при паузах сети/CPU и ускорении аудио. */ #include #include "../src/call/call_video_playout.h" #include "../lib/debug_config.h" #define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "playout test line=%d: %s", __LINE__, #x); exit(1); } } while (0) static const int64_t origin = 1000000; static void simulate(unsigned pause_ms, int cpu, int64_t quantum, int accelerated_audio) { struct call_video_playout p; call_video_playout_init(&p, 7); enum { COUNT = 240 }; struct call_video_playout_frame frames[COUNT]; for (int i = 0; i < COUNT; ++i) { int64_t arrival = origin + i * 33333ll; if (!cpu && arrival >= origin + 1000000 && arrival < origin + 1000000 + pause_ms * 1000ll) arrival = origin + 1000000 + pause_ms * 1000ll; frames[i] = (struct call_video_playout_frame){origin + i * 33333ll, arrival}; } unsigned shown = 0, accelerated = 0; int64_t last_show = 0, recovered_lag = INT64_MAX, audio_pts = origin; for (int64_t now = origin; now < origin + 12000000 && shown < COUNT; now += quantum) { uint32_t rate = accelerated_audio && now >= origin + 1500000 && now < origin + 2500000 ? 1100000 : 1000000; if (now > origin) audio_pts += quantum * rate / 1000000; if (cpu && now >= origin + 1000000 && now < origin + 1000000 + pause_ms * 1000ll) continue; struct call_clock_snapshot audio = {.pts_us = audio_pts, .rate_ppm = rate, .valid = !(now >= origin + 4000000 && now < origin + 4100000)}; struct call_video_playout_frame *f = frames + shown; int action = call_video_playout_poll(&p, f->received_us <= now ? f : NULL, NULL, now, &audio); if (action == CALL_VIDEO_PLAYOUT_WAIT) continue; CHECK(action == CALL_VIDEO_PLAYOUT_SHOW && audio.valid); CHECK(!last_show || now > last_show); CHECK(f->pts_us - audio.pts_us <= 40000); if (p.speed_ppm > 1050000) accelerated++; if (now >= origin + 6000000) recovered_lag = audio.pts_us - f->pts_us; last_show = now; shown++; // Даже готовая очередь после фриза не выдаётся целиком за один вызов worker. if (shown < COUNT && frames[shown].received_us <= now) CHECK(call_video_playout_poll(&p, frames + shown, NULL, now, &audio) == CALL_VIDEO_PLAYOUT_WAIT); } CHECK(shown == COUNT && accelerated && recovered_lag < 50000); DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS sequential recovery pause_ms=%u cpu=%d quantum_us=%lld audio_fast=%d shown=%u/%u lag_us=%lld", pause_ms, cpu, (long long)quantum, accelerated_audio, shown, COUNT, (long long)recovered_lag); } static void order_and_clock(void) { struct call_video_playout p; call_video_playout_init(&p, 9); struct call_video_playout_frame f = {origin, origin}; struct call_clock_snapshot audio = {.pts_us = origin, .rate_ppm = 1000000}; CHECK(call_video_playout_poll(&p, &f, NULL, origin, &audio) == CALL_VIDEO_PLAYOUT_WAIT); audio.valid = 1; CHECK(call_video_playout_poll(&p, &f, NULL, origin, &audio) == CALL_VIDEO_PLAYOUT_SHOW); CHECK(call_video_playout_poll(&p, &f, NULL, origin + 1000, &audio) == CALL_VIDEO_PLAYOUT_DROP_ORDER); f.pts_us += 33333; audio.valid = 0; CHECK(call_video_playout_poll(&p, &f, NULL, origin + 200000, &audio) == CALL_VIDEO_PLAYOUT_WAIT); audio.valid = 1; audio.pts_us += 200000; CHECK(call_video_playout_poll(&p, &f, NULL, origin + 200000, &audio) == CALL_VIDEO_PLAYOUT_SHOW); } int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); const unsigned pauses[] = {50, 100, 200, 500}; for (unsigned i = 0; i < sizeof(pauses) / sizeof(pauses[0]); ++i) for (int cpu = 0; cpu <= 1; ++cpu) { simulate(pauses[i], cpu, 1000, 0); simulate(pauses[i], cpu, 15625, 1); } order_and_clock(); return 0; }