From 36e8ab228ccaa04ea51ffac8526e17da955d7e61 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 20 Oct 2026 01:23:21 +0200 Subject: [PATCH] Verify startup codec preparation keeps GUI responsive and reuses probes --- tools/chatgui/CMakeLists.txt | 2 +- tools/chatgui/tests/test_call_video_clock.cpp | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index 8daca927..9dbab5a7 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -168,7 +168,7 @@ if(FFMPEG_FOUND) add_executable(test_call_video_clock tests/test_call_video_clock.cpp src/call_video_engine.cpp) target_link_libraries(test_call_video_clock PRIVATE chatgui_live_video) foreach(symbol call_audio_try_clock call_video_port_received call_video_port_open call_video_port_close call_video_port_status - call_video_port_pull call_video_port_request_key call_video_port_decoded avcodec_receive_frame) + call_video_port_pull call_video_port_request_key call_video_port_decoded avcodec_receive_frame avcodec_open2) target_link_options(test_call_video_clock PRIVATE "-Wl,--wrap=${symbol}") endforeach() add_test(NAME test_call_video_clock COMMAND test_call_video_clock) diff --git a/tools/chatgui/tests/test_call_video_clock.cpp b/tools/chatgui/tests/test_call_video_clock.cpp index 5a7a70f8..2af0e9a4 100644 --- a/tools/chatgui/tests/test_call_video_clock.cpp +++ b/tools/chatgui/tests/test_call_video_clock.cpp @@ -25,10 +25,15 @@ static int64_t start; static bool disturbed = false; static bool slowDecoder = false, holdAck = false, limitedDisplay = false; static std::atomic decodedFrames{0}; +static std::atomic codecOpens{0}; static std::atomic displayThread{0}; static std::atomic unsafeTimer{false}; static size_t threadId() { return std::hash{}(std::this_thread::get_id()); } static int64_t nowUs() { return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); } +extern "C" int __real_avcodec_open2(AVCodecContext*, const AVCodec*, AVDictionary**); +extern "C" int __wrap_avcodec_open2(AVCodecContext* context, const AVCodec* codec, AVDictionary** options) { + ++codecOpens; return __real_avcodec_open2(context, codec, options); +} extern "C" int __real_avcodec_receive_frame(AVCodecContext*, AVFrame*); extern "C" int __wrap_avcodec_receive_frame(AVCodecContext* codec, AVFrame* frame) { if (threadId() == displayThread.load()) unsafeTimer = true; @@ -110,6 +115,27 @@ static bool queuePressure() { int main(int argc, char** argv) { QCoreApplication app(argc, argv); debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_CALL, DEBUG_LEVEL_DEBUG); + // Как при старте GUI: подготовка в worker, event loop работает, повторный вызов не открывает codec. + std::atomic preparing{true}, prepared{false}; + call_video_caps ready{}; + std::thread warmup([&] { ready = CallVideoEngine::capabilities(&preparing); prepared = true; }); + QTimer pulse; pulse.setInterval(10); unsigned ticks = 0; + QObject::connect(&pulse, &QTimer::timeout, [&] { ++ticks; }); pulse.start(); + const int64_t warming = nowUs(); + while (!prepared && nowUs() - warming < 10000000) { app.processEvents(); QThread::msleep(1); } + preparing = false; warmup.join(); pulse.stop(); + if (!ticks || !ready.decode[0].profiles || !ready.decode[1].profiles || !codecOpens) return 1; + const unsigned opened = codecOpens; + const auto cancelled = CallVideoEngine::capabilities(&preparing); + if (cancelled.encode[0].profiles || cancelled.decode[0].profiles) return 1; + for (int i = 0; i < 3; ++i) { + const auto cached = CallVideoEngine::capabilities(); + uint8_t expected[CALL_VIDEO_CAPS_BYTES], actual[CALL_VIDEO_CAPS_BYTES]; + if (call_video_caps_encode(&ready, expected) || call_video_caps_encode(&cached, actual) + || memcmp(expected, actual, sizeof(actual)) || codecOpens != opened) return 1; + } + DEBUG_INFO(DEBUG_CATEGORY_CALL, "PASS video background preparation elapsed_us=%lld gui_ticks=%u codec_opens=%u; " + "cancelled access preserves cache, three warm accesses open no codecs", (long long)(nowUs() - warming), ticks, opened); if (!queuePressure()) return 1; CallVideoEncoder encoder; if (!encoder.open(format, 1000000, "libx264")) return 2; QImage image(640, 360, QImage::Format_RGB32); image.fill(Qt::blue);