From 54b50f417781a6ea21ecde7545d477e9d2e5b649 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 18 Oct 2026 22:05:33 +0200 Subject: [PATCH] Define stream QP ceiling and smooth bitrate feedback controller --- tools/chatgui/src/live_video_encoder.h | 3 +++ tools/chatgui/src/stream_qp_control.h | 31 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tools/chatgui/src/stream_qp_control.h diff --git a/tools/chatgui/src/live_video_encoder.h b/tools/chatgui/src/live_video_encoder.h index 6f9b0038..c5703947 100644 --- a/tools/chatgui/src/live_video_encoder.h +++ b/tools/chatgui/src/live_video_encoder.h @@ -17,6 +17,7 @@ struct LiveVideoConfig { int width = 1280, height = 720, fps = 30, bitrate = 2500000, codec = 1, rotation = 0; LiveVideoProfile profile = LiveVideoProfile::Automatic; int level = 0, references = 0, vbvBits = 0; // level: AVC level_idc / HEVC general_level_idc; 0 — auto. + int maxQp = -1; // Потолок QP для H.264/H.265: 0..51, -1 — настройки encoder. Профили звонка не используют. QString backend; // Пусто — проба доступных; непусто — только уже проверенный encoder. bool operator==(const LiveVideoConfig&) const = default; }; @@ -28,6 +29,8 @@ public: ~LiveVideoEncoder(); bool open(const LiveVideoConfig& config, Backend backend = Backend::Automatic); bool setBitrate(int bitrate); // true — изменение на следующем кадре; false — нужна смена видеоконтекста + bool canSetMaxQp() const; // Поддерживается изменение потолка без пересоздания encoder. + bool setMaxQp(int qp); // 0..51, применяется при следующей подаче кадра; false — ошибка/нет поддержки. void close(); bool encode(const QImage& image, uint64_t pts, bool key, QList& output); QString error() const; diff --git a/tools/chatgui/src/stream_qp_control.h b/tools/chatgui/src/stream_qp_control.h new file mode 100644 index 00000000..a17feff7 --- /dev/null +++ b/tools/chatgui/src/stream_qp_control.h @@ -0,0 +1,31 @@ +/* Плавно меняет потолок QP по выходному видеобитрейту. Один video worker, время в микросекундах. + * reset вызывается при смене пользовательских настроек/encoder. update учитывает только новые encoded bytes. + * Выбранный потолок — нижняя граница адаптации: при перерасходе допускается рост до 51, затем возврат. */ +#pragma once +#include + +class StreamQpControl { + int m_base = -1, m_limit = -1, m_target = 0; + bool m_adaptive = false, m_sampled = false; + int64_t m_at = 0; + uint64_t m_bytes = 0, m_bitrate = 0; +public: + void reset(int base, int target, bool adaptive, int64_t now) { + m_base = m_limit = base; m_target = target; m_adaptive = adaptive; + m_at = now; m_bytes = m_bitrate = 0; m_sampled = false; + } + int limit() const { return m_limit; } + uint64_t bitrate() const { return m_bitrate; } // Сглаженный выходной битрейт, бит/с. + int update(uint64_t bytes, int64_t now) { + m_bytes += bytes; + const int64_t elapsed = now - m_at; + if (elapsed < 2000000) return m_limit; + const uint64_t measured = m_bytes * 8000000 / elapsed; + m_bitrate = m_sampled ? (m_bitrate + measured) / 2 : measured; + m_sampled = true; m_bytes = 0; m_at = now; + if (!m_adaptive || m_base < 0 || m_target <= 0) return m_limit; + if (m_bitrate > uint64_t(m_target) * 108 / 100 && m_limit < 51) ++m_limit; + else if (m_bitrate < uint64_t(m_target) * 90 / 100 && m_limit > m_base) --m_limit; + return m_limit; + } +};