Document sequential presentation diagnostics and simplify GUI frame ownership

This commit is contained in:
evgeny committed 2026-10-19 17:39:54 +02:00
1 parent 4ed2c4bbab
commit a9b91145a6
3 files changed
+81 -92

No files matched your search

+54 -54
View File
@@ -386,61 +386,61 @@ bool LiveVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList
const bool constrained = m->config.profile != LiveVideoProfile::Automatic;
qint64 preparedUs = 0, submittedUs = 0;
if (!image.isNull()) {
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,
(canvas.height() - fitted.height()) / 2), fitted), image); }
result = av_frame_make_writable(m->frame);
m->scale = sws_getCachedContext(m->scale, canvas.width(), canvas.height(), AV_PIX_FMT_RGB32,
m->frame->width, m->frame->height, static_cast<AVPixelFormat>(m->frame->format), SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
if (!m->scale) result = AVERROR(ENOMEM);
if (result >= 0) {
const int* coefficients = sws_getCoefficients(SWS_CS_ITU709);
sws_setColorspaceDetails(m->scale, coefficients, 1, coefficients, 0, 0, 1 << 16, 1 << 16);
const uint8_t* input[4] = {canvas.constBits(), nullptr, nullptr, nullptr};
int stride[4] = {static_cast<int>(canvas.bytesPerLine()), 0, 0, 0};
result = sws_scale(m->scale, input, stride, 0, canvas.height(), m->frame->data, m->frame->linesize);
}
if (result >= 0 && m->reportTimer.elapsed() >= 1000 && debug_should_output(DEBUG_LEVEL_TRACE, DEBUG_CATEGORY_DEBUG)
&& (m->frame->format == AV_PIX_FMT_NV12 || m->frame->format == AV_PIX_FMT_YUV420P)) {
uint64_t edges = 0, samples = 0;
int minimum = 255, maximum = 0;
for (int y = 0; y + 1 < m->frame->height; y += 16) {
const uint8_t* row = m->frame->data[0] + y * m->frame->linesize[0];
for (int x = 0; x + 1 < m->frame->width; x += 16) {
int value = row[x]; minimum = std::min(minimum, value); maximum = std::max(maximum, value);
edges += std::abs(value - row[x + 1]) + std::abs(value - row[x + m->frame->linesize[0]]); samples += 2;
}
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,
(canvas.height() - fitted.height()) / 2), fitted), image); }
result = av_frame_make_writable(m->frame);
m->scale = sws_getCachedContext(m->scale, canvas.width(), canvas.height(), AV_PIX_FMT_RGB32,
m->frame->width, m->frame->height, static_cast<AVPixelFormat>(m->frame->format), SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
if (!m->scale) result = AVERROR(ENOMEM);
if (result >= 0) {
const int* coefficients = sws_getCoefficients(SWS_CS_ITU709);
sws_setColorspaceDetails(m->scale, coefficients, 1, coefficients, 0, 0, 1 << 16, 1 << 16);
const uint8_t* input[4] = {canvas.constBits(), nullptr, nullptr, nullptr};
int stride[4] = {static_cast<int>(canvas.bytesPerLine()), 0, 0, 0};
result = sws_scale(m->scale, input, stride, 0, canvas.height(), m->frame->data, m->frame->linesize);
}
DEBUG_TRACE(DEBUG_CATEGORY_DEBUG,
"video encoder input name=%s pts=%llu source=%dx%d encoded=%dx%d pixel=%d luma_min=%d luma_max=%d edge_x1000=%llu",
qPrintable(m->name), (unsigned long long)pts, image.width(), image.height(), m->frame->width, m->frame->height,
m->frame->format, minimum, maximum, (unsigned long long)(samples ? edges * 1000 / samples : 0));
}
if (constrained && m->timestamps.size() >= 8) {
m->error = "GPU encoder exceeded the bounded input pipeline";
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "call encoder stalled name=%s pending=%zu pts=%llu",
qPrintable(m->name), m->timestamps.size(), (unsigned long long)pts); return false;
}
bool forceKey = key && (m->forcedKeys || m->submittedFrames == 0);
m->frame->pts = av_rescale_q(static_cast<int64_t>(pts), AVRational{1, 1000000}, m->codec->time_base);
m->frame->pict_type = forceKey ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;
if (forceKey) m->frame->flags |= AV_FRAME_FLAG_KEY; else m->frame->flags &= ~AV_FRAME_FLAG_KEY;
AVFrame* hardware = nullptr;
if (result >= 0 && m->codec->hw_frames_ctx) {
hardware = av_frame_alloc();
result = hardware ? av_hwframe_get_buffer(m->codec->hw_frames_ctx, hardware, 0) : AVERROR(ENOMEM);
if (result >= 0) result = av_hwframe_transfer_data(hardware, m->frame, 0);
if (result >= 0) result = av_frame_copy_props(hardware, m->frame);
}
preparedUs = timer.nsecsElapsed() / 1000;
if (result >= 0) result = avcodec_send_frame(m->codec, hardware ? hardware : m->frame);
submittedUs = timer.nsecsElapsed() / 1000;
if (result >= 0) {
m->submittedFrames++;
if (constrained) m->timestamps.emplace_back(m->frame->pts, pts);
}
av_frame_free(&hardware);
if (result >= 0 && m->reportTimer.elapsed() >= 1000 && debug_should_output(DEBUG_LEVEL_TRACE, DEBUG_CATEGORY_DEBUG)
&& (m->frame->format == AV_PIX_FMT_NV12 || m->frame->format == AV_PIX_FMT_YUV420P)) {
uint64_t edges = 0, samples = 0;
int minimum = 255, maximum = 0;
for (int y = 0; y + 1 < m->frame->height; y += 16) {
const uint8_t* row = m->frame->data[0] + y * m->frame->linesize[0];
for (int x = 0; x + 1 < m->frame->width; x += 16) {
int value = row[x]; minimum = std::min(minimum, value); maximum = std::max(maximum, value);
edges += std::abs(value - row[x + 1]) + std::abs(value - row[x + m->frame->linesize[0]]); samples += 2;
}
}
DEBUG_TRACE(DEBUG_CATEGORY_DEBUG,
"video encoder input name=%s pts=%llu source=%dx%d encoded=%dx%d pixel=%d luma_min=%d luma_max=%d edge_x1000=%llu",
qPrintable(m->name), (unsigned long long)pts, image.width(), image.height(), m->frame->width, m->frame->height,
m->frame->format, minimum, maximum, (unsigned long long)(samples ? edges * 1000 / samples : 0));
}
if (constrained && m->timestamps.size() >= 8) {
m->error = "GPU encoder exceeded the bounded input pipeline";
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "call encoder stalled name=%s pending=%zu pts=%llu",
qPrintable(m->name), m->timestamps.size(), (unsigned long long)pts); return false;
}
bool forceKey = key && (m->forcedKeys || m->submittedFrames == 0);
m->frame->pts = av_rescale_q(static_cast<int64_t>(pts), AVRational{1, 1000000}, m->codec->time_base);
m->frame->pict_type = forceKey ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;
if (forceKey) m->frame->flags |= AV_FRAME_FLAG_KEY; else m->frame->flags &= ~AV_FRAME_FLAG_KEY;
AVFrame* hardware = nullptr;
if (result >= 0 && m->codec->hw_frames_ctx) {
hardware = av_frame_alloc();
result = hardware ? av_hwframe_get_buffer(m->codec->hw_frames_ctx, hardware, 0) : AVERROR(ENOMEM);
if (result >= 0) result = av_hwframe_transfer_data(hardware, m->frame, 0);
if (result >= 0) result = av_frame_copy_props(hardware, m->frame);
}
preparedUs = timer.nsecsElapsed() / 1000;
if (result >= 0) result = avcodec_send_frame(m->codec, hardware ? hardware : m->frame);
submittedUs = timer.nsecsElapsed() / 1000;
if (result >= 0) {
m->submittedFrames++;
if (constrained) m->timestamps.emplace_back(m->frame->pts, pts);
}
av_frame_free(&hardware);
} else if (finish) result = avcodec_send_frame(m->codec, nullptr);
AVPacket* packet = av_packet_alloc();
if (!packet) result = AVERROR(ENOMEM);