From 03438257468dd80420ebfad89ffa1fc1a6aa9384 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 18 Oct 2026 16:57:49 +0200 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BA=D0=B0=D0=B7=D1=8B=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D1=84=D0=B0=D0=BA=D1=82=D0=B8=D1=87=D0=B5?= =?UTF-8?q?=D1=81=D0=BA=D1=83=D1=8E=20=D0=B3=D0=BB=D1=83=D0=B1=D0=B8=D0=BD?= =?UTF-8?q?=D1=83=20GPU-=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D0=B9=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=20=D0=B1=D0=B5=D0=B7=20=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D0=B9=20TX-?= =?UTF-8?q?=D0=BE=D1=87=D0=B5=D1=80=D0=B5=D0=B4=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/chatgui/src/call_video_engine.cpp | 39 ++++++++++++------------ tools/chatgui/src/live_video_encoder.cpp | 11 ++++--- tools/chatgui/src/live_video_encoder.h | 5 ++- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/tools/chatgui/src/call_video_engine.cpp b/tools/chatgui/src/call_video_engine.cpp index 0375f3b4..15c137de 100644 --- a/tools/chatgui/src/call_video_engine.cpp +++ b/tools/chatgui/src/call_video_engine.cpp @@ -251,26 +251,25 @@ void CallVideoEngine::transmit() { if (!encoded) { forceKey = true; m->report(encoder.error()); continue; } // GPU может вернуть несколько уже готовых кадров: отправляем все, не добавляя очередь в TX worker. for (const auto& output : outputs) { - ++encodedCount; if (output.key) ++keyCount; - call_video_frame packet{}; - packet.call_id = m->id; packet.frame_id = ++frameId; packet.pts_us = output.pts; packet.format = format; - packet.size = packet.length = output.data.size(); packet.data = reinterpret_cast(output.data.constData()); - packet.flags = output.key ? CALL_VIDEO_KEY : 0; - { std::lock_guard guard(m->lock); - if (!m->running || !m->enabled || m->revision != revision || nowUs() - output.pts > CallCameraMaxFrameAgeUs) { - DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX discards obsolete encoded frame revision=%llu/%llu enabled=%d age_us=%lld", - (unsigned long long)revision, (unsigned long long)m->revision, m->enabled, (long long)(nowUs() - output.pts)); - ++expired; - forceKey = true; continue; - } - forceKey = call_video_port_send(&packet) != 1; } - // Одинаковое окно исходного PTS на всех этапах: примерно один кадр в секунду, без трассы каждого кадра. - if (output.pts % 1000000 < 34000) - DEBUG_DEBUG(DEBUG_CATEGORY_CALL, - "video timing stage=encode call=%llu frame=%llu pts=%lld mono_us=%lld encode_us=%lld accepted=%d", - (unsigned long long)m->id, (unsigned long long)frameId, (long long)output.pts, (long long)nowUs(), - (long long)(nowUs() - encodeStarted), !forceKey); - if (forceKey) { ++sendBusy; break; } + ++encodedCount; if (output.key) ++keyCount; + call_video_frame packet{}; + packet.call_id = m->id; packet.frame_id = ++frameId; packet.pts_us = output.pts; packet.format = format; + packet.size = packet.length = output.data.size(); packet.data = reinterpret_cast(output.data.constData()); + packet.flags = output.key ? CALL_VIDEO_KEY : 0; + { std::lock_guard guard(m->lock); + if (!m->running || !m->enabled || m->revision != revision || nowUs() - output.pts > CallCameraMaxFrameAgeUs) { + DEBUG_WARN(DEBUG_CATEGORY_CALL, "video TX discards obsolete encoded frame revision=%llu/%llu enabled=%d age_us=%lld", + (unsigned long long)revision, (unsigned long long)m->revision, m->enabled, (long long)(nowUs() - output.pts)); + ++expired; forceKey = true; continue; + } + forceKey = call_video_port_send(&packet) != 1; } + // Одинаковое окно исходного PTS на всех этапах: примерно один кадр в секунду, без трассы каждого кадра. + if (output.pts % 1000000 < 34000) + DEBUG_DEBUG(DEBUG_CATEGORY_CALL, + "video timing stage=encode call=%llu frame=%llu pts=%lld mono_us=%lld encode_us=%lld accepted=%d", + (unsigned long long)m->id, (unsigned long long)frameId, (long long)output.pts, (long long)nowUs(), + (long long)(nowUs() - encodeStarted), !forceKey); + if (forceKey) { ++sendBusy; break; } } #else idle(); diff --git a/tools/chatgui/src/live_video_encoder.cpp b/tools/chatgui/src/live_video_encoder.cpp index f778bf3c..a5df8ef3 100644 --- a/tools/chatgui/src/live_video_encoder.cpp +++ b/tools/chatgui/src/live_video_encoder.cpp @@ -209,7 +209,8 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config, Backend backend) { } else if (name.endsWith("_amf")) { av_dict_set(&options, "usage", "ultralowlatency", 0); av_dict_set(&options, "quality", "speed", 0); - av_dict_set(&options, "async_depth", "1", 0); + bool asyncDepth = av_opt_find(c->priv_data, "async_depth", nullptr, 0, 0); + if (asyncDepth) av_dict_set(&options, "async_depth", "1", 0); bool forceIdr = config.codec == STREAM_MEDIA_H264 || config.codec == STREAM_MEDIA_H265; m->forcedKeys = forceIdr && av_opt_find(c->priv_data, "forced_idr", nullptr, 0, 0); if (m->forcedKeys) av_dict_set(&options, "forced_idr", "1", 0); @@ -220,8 +221,8 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config, Backend backend) { if (config.codec == STREAM_MEDIA_H265) av_dict_set(&options, "profile_tier", "main", 0); } DEBUG_INFO(DEBUG_CATEGORY_VIDEO, - "desktop AMF low latency settings name=%s usage=ultralowlatency quality=speed async_depth=1 forced_idr=%d", - name.constData(), m->forcedKeys); + "desktop AMF low latency settings name=%s usage=ultralowlatency quality=speed async_depth=%s forced_idr=%d", + name.constData(), asyncDepth ? "1" : "driver", m->forcedKeys); } else if (name == "libvpx-vp9" || name == "libaom-av1") { av_dict_set(&options, "deadline", "realtime", 0); av_dict_set(&options, "cpu-used", "8", 0); av_dict_set(&options, "lag-in-frames", "0", 0); av_dict_set(&options, "row-mt", "1", 0); @@ -443,11 +444,11 @@ bool LiveVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList if (m->reportTimer.elapsed() >= 1000) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "video encoder flow name=%s size=%dx%d bitrate=%d interval_us=%lld inputs=%u outputs=%u keys=%u " - "prepare_max_us=%lld submit_max_us=%lld drain_max_us=%lld key_max_us=%lld delta_max_us=%lld pts=%llu", + "prepare_max_us=%lld submit_max_us=%lld drain_max_us=%lld key_max_us=%lld delta_max_us=%lld pending=%zu pts=%llu", qPrintable(m->name), m->config.width, m->config.height, m->config.bitrate, (long long)(m->reportTimer.nsecsElapsed() / 1000), m->inputs, m->outputs, m->keys, (long long)m->prepareMax, (long long)m->submitMax, (long long)m->drainMax, - (long long)m->keyMax, (long long)m->deltaMax, (unsigned long long)pts); + (long long)m->keyMax, (long long)m->deltaMax, m->timestamps.size(), (unsigned long long)pts); m->reportTimer.restart(); m->inputs = m->outputs = m->keys = 0; m->slowReported = false; m->prepareMax = m->submitMax = m->drainMax = m->keyMax = m->deltaMax = 0; diff --git a/tools/chatgui/src/live_video_encoder.h b/tools/chatgui/src/live_video_encoder.h index 5123870a..6f9b0038 100644 --- a/tools/chatgui/src/live_video_encoder.h +++ b/tools/chatgui/src/live_video_encoder.h @@ -1,6 +1,9 @@ /* FFmpeg live encoder: один worker владеет контекстом, конвертером и decoder configuration. * open/encode/close последовательны; каждый KEY содержит uAV2 envelope. Пакеты имеют capture PTS. - * Backend ограничивает перебор: HardwareOnly никогда не открывает software encoder. */ + * Backend ограничивает перебор: HardwareOnly никогда не открывает software encoder. + * Профили звонка проверяются по bitstream; допустим задержанный выход GPU без B-кадров. + * encode выдаёт все готовые кадры сразу. До восьми входных PTS сохраняются до выхода; + * QSV использует 90kHz внутри codec, наружу возвращается точный исходный capture PTS. */ #pragma once #include #include