From f7853f145cd29706df5542528dc19c314cb02b84 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 18 Oct 2026 19:44:38 +0200 Subject: [PATCH] Trace HEVC key frame quantization and reduce AMF log noise --- src/video/video.c | 38 ++++++++++++++++++------ tools/chatgui/src/live_video_encoder.cpp | 33 +++++++++++++++++++- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/src/video/video.c b/src/video/video.c index 5ddf95f2..124af57b 100644 --- a/src/video/video.c +++ b/src/video/video.c @@ -15,6 +15,7 @@ #ifdef HAVE_FFMPEG #include #include +#include #include #include #include @@ -66,24 +67,43 @@ int video_extract_thumbnail(const char* src, const char* jpg_out, int max_w) { static pthread_once_t g_ffmpeg_once = PTHREAD_ONCE_INIT; -/* AMF передаёт свой внутренний трейс через AV_LOG_DEBUG. Остальные подробные логи FFmpeg отсекаем. */ +/* Пропускаем диагностику AMF и существенные поля trace_headers, остальные подробности FFmpeg отсекаем. */ static void video_av_log_cb(void* avcl, int level, const char* fmt, va_list vl) { const char *encoder = NULL; + int headers = 0; if (level > AV_LOG_WARNING) { if (!debug_should_output(DEBUG_LEVEL_TRACE, DEBUG_CATEGORY_DEBUG)) return; - if (!avcl || *(const AVClass **)avcl != avcodec_get_class()) return; - const AVCodecContext *codec = avcl; - if (!codec->codec || !codec->codec->name) return; - const char *name = codec->codec->name; - size_t length = strlen(name); - if (length < 4 || strcmp(name + length - 4, "_amf")) return; - encoder = name; + if (!avcl) return; + if (*(const AVClass **)avcl == av_bsf_get_class()) { + const AVBSFContext *filter = avcl; + if (!filter->filter || strcmp(filter->filter->name, "trace_headers")) return; + headers = 1; + } else { + if (*(const AVClass **)avcl != avcodec_get_class()) return; + const AVCodecContext *codec = avcl; + if (!codec->codec || !codec->codec->name) return; + const char *name = codec->codec->name; + size_t length = strlen(name); + if (length < 4 || strcmp(name + length - 4, "_amf")) return; + encoder = name; + } } char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, vl); size_t len = strlen(buf); while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) buf[--len] = '\0'; - if (encoder) DEBUG_TRACE(DEBUG_CATEGORY_DEBUG, "ffmpeg AMF encoder=%s: %s", encoder, buf); + if (headers) { + if (!strstr(buf, "Packet:") && !strstr(buf, "init_qp_minus26") && !strstr(buf, "slice_qp_delta") + && !strstr(buf, "pic_width_in_luma_samples") && !strstr(buf, "pic_height_in_luma_samples") + && !strstr(buf, "pic_parameter_set_id") && !strstr(buf, "cu_qp_delta_enabled_flag") + && !strstr(buf, "diff_cu_qp_delta_depth") && !strstr(buf, "slice_type")) return; + DEBUG_TRACE(DEBUG_CATEGORY_DEBUG, "ffmpeg headers: %s", buf); + } else if (encoder) { + if (!strstr(buf, "Warning:") && !strstr(buf, "Error:") + && (strstr(buf, "::SubmitInput() : format") || strstr(buf, "SubmitToEncoder() End: frame pts=") + || strstr(buf, "::QueryOutput()") || strstr(buf, "QueryOutput() End: frame pts="))) return; + DEBUG_TRACE(DEBUG_CATEGORY_DEBUG, "ffmpeg AMF encoder=%s: %s", encoder, buf); + } else if (level <= AV_LOG_ERROR) DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "ffmpeg: %s", buf); else DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "ffmpeg: %s", buf); } diff --git a/tools/chatgui/src/live_video_encoder.cpp b/tools/chatgui/src/live_video_encoder.cpp index ed1a7ce1..a4d7fd08 100644 --- a/tools/chatgui/src/live_video_encoder.cpp +++ b/tools/chatgui/src/live_video_encoder.cpp @@ -32,11 +32,13 @@ struct LiveVideoEncoder::Impl { #ifdef HAVE_FFMPEG AVCodecContext* codec = nullptr; AVBSFContext* filter = nullptr; + AVBSFContext* headers = nullptr; // Только чтение заголовков KEY при debug=trace, исходный пакет не меняется. + bool headersAttempted = false; AVFrame* frame = nullptr; AVBufferRef* device = nullptr; SwsContext* scale = nullptr; ~Impl() { - av_bsf_free(&filter); avcodec_free_context(&codec); av_frame_free(&frame); + av_bsf_free(&headers); av_bsf_free(&filter); avcodec_free_context(&codec); av_frame_free(&frame); av_buffer_unref(&device); sws_freeContext(scale); } #endif @@ -423,6 +425,35 @@ bool LiveVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList if (result >= 0) result = av_bsf_receive_packet(m->filter, packet); if (result < 0) break; } + if (m->config.codec == STREAM_MEDIA_H265 && (packet->flags & AV_PKT_FLAG_KEY) + && debug_should_output(DEBUG_LEVEL_TRACE, DEBUG_CATEGORY_DEBUG)) { + int diagnostic = 0; + if (!m->headersAttempted) { + m->headersAttempted = true; + const auto* trace = av_bsf_get_by_name("trace_headers"); + diagnostic = trace ? av_bsf_alloc(trace, &m->headers) : AVERROR(ENOSYS); + if (diagnostic >= 0) diagnostic = m->filter ? avcodec_parameters_copy(m->headers->par_in, m->filter->par_out) + : avcodec_parameters_from_context(m->headers->par_in, m->codec); + if (diagnostic >= 0) { + m->headers->time_base_in = m->codec->time_base; + diagnostic = av_bsf_init(m->headers); + } + if (diagnostic >= 0) DEBUG_INFO(DEBUG_CATEGORY_DEBUG, + "HEVC KEY header diagnostics enabled name=%s; slice_QP=26+init_qp_minus26+slice_qp_delta", + qPrintable(m->name)); + } + if (m->headers && diagnostic >= 0) { + AVPacket* copy = av_packet_clone(packet); + diagnostic = copy ? av_bsf_send_packet(m->headers, copy) : AVERROR(ENOMEM); + if (diagnostic >= 0) diagnostic = av_bsf_receive_packet(m->headers, copy); + av_packet_free(©); + } + if (diagnostic < 0) { + DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "HEVC header diagnostics disabled name=%s pts=%lld error=%s", + qPrintable(m->name), (long long)packet->pts, qPrintable(ffError(diagnostic))); + av_bsf_free(&m->headers); + } + } LiveEncodedFrame encoded; encoded.key = (packet->flags & AV_PKT_FLAG_KEY) != 0; encoded.pts = packet->pts >= 0 ? static_cast(packet->pts) : pts; if (constrained) {