Диагностика задержек программного видео на desktop

This commit is contained in:
evgeny committed 2026-10-18 16:17:51 +02:00
1 parent 03ff431d78
commit 5a55bcb500
3 files changed
+51 -11

No files matched your search

+30
View File
@@ -1,5 +1,6 @@
#include "live_video_encoder.h"
#include "debug_config.h"
#include <QElapsedTimer>
#include <QPainter>
#include <algorithm>
#include <cstring>
@@ -18,6 +19,9 @@ struct LiveVideoEncoder::Impl {
QString error, name;
LiveVideoConfig config;
int64_t submittedFrames = 0;
QElapsedTimer reportTimer;
unsigned inputs = 0, outputs = 0, keys = 0;
qint64 prepareMax = 0, submitMax = 0, drainMax = 0, keyMax = 0, deltaMax = 0;
#ifdef HAVE_FFMPEG
AVCodecContext* codec = nullptr;
AVBSFContext* filter = nullptr;
@@ -297,6 +301,9 @@ bool LiveVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList
if (!m->codec || !m->frame || image.isNull() || pts > INT64_MAX) {
m->error = "Invalid video frame"; DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop encode invalid frame/state"); return false;
}
QElapsedTimer timer; timer.start();
if (!m->reportTimer.isValid()) m->reportTimer.start();
const auto outputBase = output.size();
QImage canvas(m->config.width, m->config.height, QImage::Format_RGB32); canvas.fill(Qt::black);
QSize fitted = image.size().scaled(canvas.size(), Qt::KeepAspectRatio);
{ QPainter painter(&canvas); painter.drawImage(QRect(QPoint((canvas.width() - fitted.width()) / 2,
@@ -325,7 +332,9 @@ bool LiveVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList
if (result >= 0) result = av_hwframe_transfer_data(hardware, m->frame, 0);
if (result >= 0) result = av_frame_copy_props(hardware, m->frame);
}
const qint64 preparedUs = timer.nsecsElapsed() / 1000;
if (result >= 0) result = avcodec_send_frame(m->codec, hardware ? hardware : m->frame);
const qint64 submittedUs = timer.nsecsElapsed() / 1000;
if (result >= 0) m->submittedFrames++;
av_frame_free(&hardware);
AVPacket* packet = av_packet_alloc();
@@ -355,6 +364,27 @@ bool LiveVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList
}
av_packet_free(&packet);
if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {
// Сводка по этапам отличает конвертацию, синхронный encoder и получение/упаковку пакета.
const qint64 totalUs = timer.nsecsElapsed() / 1000;
++m->inputs; m->outputs += output.size() - outputBase;
bool producedKey = false;
for (auto i = outputBase; i < output.size(); ++i) if (output[i].key) { ++m->keys; producedKey = true; }
m->prepareMax = std::max(m->prepareMax, preparedUs);
m->submitMax = std::max(m->submitMax, submittedUs - preparedUs);
m->drainMax = std::max(m->drainMax, totalUs - submittedUs);
auto& frameMax = producedKey ? m->keyMax : m->deltaMax;
frameMax = std::max(frameMax, totalUs);
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",
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);
m->reportTimer.restart(); m->inputs = m->outputs = m->keys = 0;
m->prepareMax = m->submitMax = m->drainMax = m->keyMax = m->deltaMax = 0;
}
if (key && output.isEmpty()) DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop requested KEY buffered name=%s PTS=%llu",
qPrintable(m->name), (unsigned long long)pts);
return true;