Показывать фактическую глубину GPU-конвейера без дополнительной TX-очереди

This commit is contained in:
evgeny committed 2026-10-18 16:57:49 +02:00
1 parent 09f48403c5
commit 0343825746
3 files changed
+29 -26

No files matched your search

+19 -20
View File
@@ -251,26 +251,25 @@ void CallVideoEngine::transmit() {
if (!encoded) { forceKey = true; m->report(encoder.error()); continue; } if (!encoded) { forceKey = true; m->report(encoder.error()); continue; }
// GPU может вернуть несколько уже готовых кадров: отправляем все, не добавляя очередь в TX worker. // GPU может вернуть несколько уже готовых кадров: отправляем все, не добавляя очередь в TX worker.
for (const auto& output : outputs) { for (const auto& output : outputs) {
++encodedCount; if (output.key) ++keyCount; ++encodedCount; if (output.key) ++keyCount;
call_video_frame packet{}; call_video_frame packet{};
packet.call_id = m->id; packet.frame_id = ++frameId; packet.pts_us = output.pts; packet.format = format; 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<const uint8_t*>(output.data.constData()); packet.size = packet.length = output.data.size(); packet.data = reinterpret_cast<const uint8_t*>(output.data.constData());
packet.flags = output.key ? CALL_VIDEO_KEY : 0; packet.flags = output.key ? CALL_VIDEO_KEY : 0;
{ std::lock_guard<std::mutex> guard(m->lock); { std::lock_guard<std::mutex> guard(m->lock);
if (!m->running || !m->enabled || m->revision != revision || nowUs() - output.pts > CallCameraMaxFrameAgeUs) { 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", 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)); (unsigned long long)revision, (unsigned long long)m->revision, m->enabled, (long long)(nowUs() - output.pts));
++expired; ++expired; forceKey = true; continue;
forceKey = true; continue; }
} forceKey = call_video_port_send(&packet) != 1; }
forceKey = call_video_port_send(&packet) != 1; } // Одинаковое окно исходного PTS на всех этапах: примерно один кадр в секунду, без трассы каждого кадра.
// Одинаковое окно исходного PTS на всех этапах: примерно один кадр в секунду, без трассы каждого кадра. if (output.pts % 1000000 < 34000)
if (output.pts % 1000000 < 34000) DEBUG_DEBUG(DEBUG_CATEGORY_CALL,
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "video timing stage=encode call=%llu frame=%llu pts=%lld mono_us=%lld encode_us=%lld accepted=%d",
"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(),
(unsigned long long)m->id, (unsigned long long)frameId, (long long)output.pts, (long long)nowUs(), (long long)(nowUs() - encodeStarted), !forceKey);
(long long)(nowUs() - encodeStarted), !forceKey); if (forceKey) { ++sendBusy; break; }
if (forceKey) { ++sendBusy; break; }
} }
#else #else
idle(); idle();
+6 -5
View File
@@ -209,7 +209,8 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config, Backend backend) {
} else if (name.endsWith("_amf")) { } else if (name.endsWith("_amf")) {
av_dict_set(&options, "usage", "ultralowlatency", 0); av_dict_set(&options, "usage", "ultralowlatency", 0);
av_dict_set(&options, "quality", "speed", 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; 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); 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); 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); if (config.codec == STREAM_MEDIA_H265) av_dict_set(&options, "profile_tier", "main", 0);
} }
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, DEBUG_INFO(DEBUG_CATEGORY_VIDEO,
"desktop AMF low latency settings name=%s usage=ultralowlatency quality=speed async_depth=1 forced_idr=%d", "desktop AMF low latency settings name=%s usage=ultralowlatency quality=speed async_depth=%s forced_idr=%d",
name.constData(), m->forcedKeys); name.constData(), asyncDepth ? "1" : "driver", m->forcedKeys);
} else if (name == "libvpx-vp9" || name == "libaom-av1") { } 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, "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); 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) { if (m->reportTimer.elapsed() >= 1000) {
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG,
"video encoder flow name=%s size=%dx%d bitrate=%d interval_us=%lld inputs=%u outputs=%u keys=%u " "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, 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->reportTimer.nsecsElapsed() / 1000), m->inputs, m->outputs, m->keys,
(long long)m->prepareMax, (long long)m->submitMax, (long long)m->drainMax, (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->reportTimer.restart(); m->inputs = m->outputs = m->keys = 0;
m->slowReported = false; m->slowReported = false;
m->prepareMax = m->submitMax = m->drainMax = m->keyMax = m->deltaMax = 0; m->prepareMax = m->submitMax = m->drainMax = m->keyMax = m->deltaMax = 0;
+4 -1
View File
@@ -1,6 +1,9 @@
/* FFmpeg live encoder: один worker владеет контекстом, конвертером и decoder configuration. /* FFmpeg live encoder: один worker владеет контекстом, конвертером и decoder configuration.
* open/encode/close последовательны; каждый KEY содержит uAV2 envelope. Пакеты имеют capture PTS. * 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 #pragma once
#include <QByteArray> #include <QByteArray>
#include <QImage> #include <QImage>