From e3b74de7ef2240f7c7de3bb1ba80ccbf81de9c00 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 20 Oct 2026 01:10:44 +0200 Subject: [PATCH] Prepare and cache desktop video codecs once at application startup --- tools/chatgui/src/call_video_engine.cpp | 38 +++++++++++++++++++++---- tools/chatgui/src/call_video_engine.h | 5 ++++ tools/chatgui/src/main.cpp | 15 ++++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/tools/chatgui/src/call_video_engine.cpp b/tools/chatgui/src/call_video_engine.cpp index 05baa788..d2f570fc 100644 --- a/tools/chatgui/src/call_video_engine.cpp +++ b/tools/chatgui/src/call_video_engine.cpp @@ -9,6 +9,7 @@ #include "../../../src/call/call_audio.h" #include "debug_config.h" #include "mem.h" +#include #include #include #include @@ -252,10 +253,26 @@ void CallVideoEngine::setVideo(bool enabled, const QByteArray& cameraId, int wid #endif } -void CallVideoEngine::transmit() { - call_video_caps caps = CallVideoDecoder::capabilities(&m->running); +call_video_caps CallVideoEngine::capabilities(const std::atomic* running) { + static std::mutex lock; + static call_video_caps verified{}; + static bool initialized = false; + const int64_t started = nowUs(); + auto cancelled = [running] { + if (!running || running->load()) return false; + DEBUG_INFO(DEBUG_CATEGORY_CALL, "video codec preparation cancelled; completed cache retained"); return true; + }; + std::unique_lock guard(lock, std::defer_lock); + while (!guard.try_lock()) { if (cancelled()) return {}; QThread::usleep(1000); } + if (cancelled()) return {}; + if (initialized) { + DEBUG_INFO(DEBUG_CATEGORY_CALL, "video codec capabilities cache hit wait_us=%lld", (long long)(nowUs() - started)); + return verified; + } + DEBUG_INFO(DEBUG_CATEGORY_CALL, "video codec preparation begin; camera and network unused"); + call_video_caps caps = CallVideoDecoder::capabilities(running); // Не связываем приём с наличием собственного encoder. - for (unsigned codec = 1; codec <= 2 && m->running; ++codec) { + for (unsigned codec = 1; codec <= 2; ++codec) { for (const QSize size : {QSize(1920, 1080), QSize(1280, 720)}) { for (unsigned fps : {30u, 50u, 60u}) { const bool fullhd = size.width() == 1920; @@ -264,14 +281,15 @@ void CallVideoEngine::transmit() { : (fullhd ? (fps > 30 ? 123 : 120) : (fps > 30 ? 120 : 93))), 4000000, (uint8_t)(codec == 1 ? CALL_VIDEO_CONSTRAINED_BASELINE : CALL_VIDEO_MAIN)}; bool bothOrientations = true; - for (int portrait = 0; portrait <= 1 && bothOrientations && m->running; ++portrait) { + for (int portrait = 0; portrait <= 1 && bothOrientations; ++portrait) { + if (cancelled()) return {}; probe.width = portrait ? size.height() : size.width(); probe.height = portrait ? size.width() : size.height(); CallVideoEncoder encoder; bothOrientations = encoder.open(probe, 1000000); if (!bothOrientations) DEBUG_WARN(DEBUG_CATEGORY_CALL, "call codec probe unavailable codec=%u size=%ux%u: %s", codec, probe.width, probe.height, qPrintable(encoder.error())); } - if (bothOrientations && m->running) { + if (bothOrientations) { auto& limit = caps.encode[codec - 1]; limit.width = std::max(limit.width, size.width()); limit.height = std::max(limit.height, size.height()); limit.fps = std::max(limit.fps, fps); @@ -284,6 +302,16 @@ void CallVideoEngine::transmit() { } if (caps.encode[codec - 1].profiles && call_video_limit_orientations(&caps.encode[codec - 1], codec)) caps.encode[codec - 1] = {}; } + if (cancelled()) return {}; + verified = caps; initialized = true; + DEBUG_INFO(DEBUG_CATEGORY_CALL, "video codec preparation complete elapsed_us=%lld encode=%u/%u decode=%u/%u", + (long long)(nowUs() - started), caps.encode[0].profiles, caps.encode[1].profiles, + caps.decode[0].profiles, caps.decode[1].profiles); + return verified; +} + +void CallVideoEngine::transmit() { + const call_video_caps caps = capabilities(&m->running); if (!m->running) return; if (call_video_port_open(m->id, &caps) < 0) { m->report(tr("Video transport is unavailable")); return; } CallVideoEncoder encoder; diff --git a/tools/chatgui/src/call_video_engine.h b/tools/chatgui/src/call_video_engine.h index 718413d1..791d06bb 100644 --- a/tools/chatgui/src/call_video_engine.h +++ b/tools/chatgui/src/call_video_engine.h @@ -3,7 +3,9 @@ #include #include #include +#include #include "call_camera_capture.h" +#include "../../../src/call/call_video.h" // Камера изолирована в процессе. TX/decoder владеют codec; отдельный playout получает готовые CPU-изображения. class CallVideoEngine : public QObject { @@ -11,6 +13,9 @@ class CallVideoEngine : public QObject { public: explicit CallVideoEngine(QObject* parent = nullptr); ~CallVideoEngine() override; + // Блокирующая подготовка только в worker: один общий кэш encoder/decoder на процесс, без камеры и сети. + // running отменяет ожидание/пробу; частичный результат не кэшируется. nullptr — без отмены. + static call_video_caps capabilities(const std::atomic* running = nullptr); void start(quint64 id); void stop(); void setVideo(bool enabled, const QByteArray& camera, int width, int height, int codec, int fps = 30); diff --git a/tools/chatgui/src/main.cpp b/tools/chatgui/src/main.cpp index 74bf51e2..c4c14578 100644 --- a/tools/chatgui/src/main.cpp +++ b/tools/chatgui/src/main.cpp @@ -19,11 +19,13 @@ #include #include "gui_language.h" #include "call_camera_capture.h" +#include "call_video_engine.h" #include "single_instance.h" #include "config_parser.h" #include #include #include +#include #ifdef _WIN32 #include #include @@ -342,6 +344,14 @@ int main(int argc, char *argv[]) { cfg.debugLevel(), cfg.debugCategories()); instance.setWindow(&w); if (minimized) w.showMinimized(); else w.show(); + // Пробы не используют камеры и сеть; будущие звонки берут общий кэш без повторной подготовки. + std::atomic prepareVideo{true}; + std::future videoPreparation; + try { + videoPreparation = std::async(std::launch::async, [&prepareVideo] { CallVideoEngine::capabilities(&prepareVideo); }); + } catch (const std::exception& error) { + DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video codec background preparation could not start: %s", error.what()); + } DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "GUI startup: minimized_requested=%d minimized=%d activation_server=%s", minimized, w.isMinimized(), qPrintable(instance.serverName())); @@ -352,6 +362,11 @@ int main(int argc, char *argv[]) { #endif int ret = app.exec(); + prepareVideo = false; + if (videoPreparation.valid()) { + try { videoPreparation.get(); } + catch (const std::exception& error) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video codec background preparation failed: %s", error.what()); } + } DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "main: app.exec() returned %d", ret); return ret; }