From dca1adc1736a7000b8a7136b289ecfe7c8311a44 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 21:43:54 +0200 Subject: [PATCH] Extract live video encoder naming for reuse by calls and broadcasts --- ...deo_encoder.cpp => live_video_encoder.cpp} | 32 +++++++++---------- ...m_video_encoder.h => live_video_encoder.h} | 20 +++++++----- 2 files changed, 28 insertions(+), 24 deletions(-) rename tools/chatgui/src/{stream_video_encoder.cpp => live_video_encoder.cpp} (93%) rename tools/chatgui/src/{stream_video_encoder.h => live_video_encoder.h} (62%) diff --git a/tools/chatgui/src/stream_video_encoder.cpp b/tools/chatgui/src/live_video_encoder.cpp similarity index 93% rename from tools/chatgui/src/stream_video_encoder.cpp rename to tools/chatgui/src/live_video_encoder.cpp index 7bdb6bb8..b79360bf 100644 --- a/tools/chatgui/src/stream_video_encoder.cpp +++ b/tools/chatgui/src/live_video_encoder.cpp @@ -1,4 +1,4 @@ -#include "stream_video_encoder.h" +#include "live_video_encoder.h" #include "debug_config.h" #include #include @@ -14,9 +14,9 @@ extern "C" { #endif } -struct StreamVideoEncoder::Impl { +struct LiveVideoEncoder::Impl { QString error, name; - StreamVideoConfig config; + LiveVideoConfig config; #ifdef HAVE_FFMPEG AVCodecContext* codec = nullptr; AVBSFContext* filter = nullptr; @@ -29,12 +29,12 @@ struct StreamVideoEncoder::Impl { } #endif }; -StreamVideoEncoder::StreamVideoEncoder() : m(std::make_unique()) {} -StreamVideoEncoder::~StreamVideoEncoder() = default; -void StreamVideoEncoder::close() { m = std::make_unique(); } -QString StreamVideoEncoder::error() const { return m->error; } -QString StreamVideoEncoder::name() const { return m->name; } -QString StreamVideoEncoder::codecName(int codec) { +LiveVideoEncoder::LiveVideoEncoder() : m(std::make_unique()) {} +LiveVideoEncoder::~LiveVideoEncoder() = default; +void LiveVideoEncoder::close() { m = std::make_unique(); } +QString LiveVideoEncoder::error() const { return m->error; } +QString LiveVideoEncoder::name() const { return m->name; } +QString LiveVideoEncoder::codecName(int codec) { static const char* names[] = {"", "H.264", "H.265", "VP9", "AV1", "H.266"}; return codec >= 1 && codec <= 5 ? QString::fromLatin1(names[codec]) : QString(); } @@ -69,7 +69,7 @@ AVPixelFormat softwareFormat(const AVCodec* codec) { } #endif -bool StreamVideoEncoder::open(const StreamVideoConfig& config) { +bool LiveVideoEncoder::open(const LiveVideoConfig& config) { close(); if (config.width < 16 || config.height < 16 || config.width > 4096 || config.height > 4096 || config.width % 2 || config.height % 2 || config.fps < 1 || config.fps > 60 @@ -152,7 +152,7 @@ bool StreamVideoEncoder::open(const StreamVideoConfig& config) { } if (!pass) { QImage probe(config.width, config.height, QImage::Format_RGB32); probe.fill(Qt::black); - QList output; + QList output; if (!encode(probe, 0, true, output) || output.isEmpty() || !output.front().key) { DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "desktop encoder probe lacks immediate KEY name=%s error=%s", name.constData(), qPrintable(m->error)); break; @@ -176,7 +176,7 @@ bool StreamVideoEncoder::open(const StreamVideoConfig& config) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop encoder open failed: %s", qPrintable(m->error)); return false; } -bool StreamVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList& output) { +bool LiveVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QList& output) { #ifdef HAVE_FFMPEG 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; @@ -217,7 +217,7 @@ bool StreamVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QLi if (result >= 0) result = av_bsf_receive_packet(m->filter, packet); if (result < 0) break; } - StreamEncodedFrame encoded; encoded.key = (packet->flags & AV_PKT_FLAG_KEY) != 0; + LiveEncodedFrame encoded; encoded.key = (packet->flags & AV_PKT_FLAG_KEY) != 0; encoded.pts = packet->pts >= 0 ? static_cast(packet->pts) : pts; if (key) DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop requested KEY name=%s input_PTS=%llu output_PTS=%llu key=%d flags=%x bytes=%d", qPrintable(m->name), (unsigned long long)pts, (unsigned long long)encoded.pts, encoded.key, packet->flags, packet->size); @@ -246,7 +246,7 @@ bool StreamVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QLi #endif } -QList StreamVideoEncoder::available() { +QList LiveVideoEncoder::available() { QList result; for (int codec = 1; codec <= 5; codec++) { #ifdef HAVE_FFMPEG @@ -254,13 +254,13 @@ QList StreamVideoEncoder::available() { for (const auto& name : encoders(codec)) if (avcodec_find_encoder_by_name(name.constData())) { installed = true; break; } if (!installed) { DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop codec has no installed encoder codec=%d", codec); continue; } #endif - StreamVideoEncoder encoder; StreamVideoConfig config; config.width = 320; config.height = 180; config.codec = codec; + LiveVideoEncoder encoder; LiveVideoConfig config; config.width = 320; config.height = 180; config.codec = codec; if (encoder.open(config)) result.push_back(codec); } return result; } -bool StreamVideoEncoder::setBitrate(int bitrate) { +bool LiveVideoEncoder::setBitrate(int bitrate) { if (bitrate < 64000 || bitrate > 16000000) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop live bitrate invalid value=%d", bitrate); return false; } #ifdef HAVE_FFMPEG // libx264 перечитывает эти три значения через reconfig_encoder при подаче следующего кадра. diff --git a/tools/chatgui/src/stream_video_encoder.h b/tools/chatgui/src/live_video_encoder.h similarity index 62% rename from tools/chatgui/src/stream_video_encoder.h rename to tools/chatgui/src/live_video_encoder.h index 11578696..b1adb86b 100644 --- a/tools/chatgui/src/stream_video_encoder.h +++ b/tools/chatgui/src/live_video_encoder.h @@ -9,19 +9,23 @@ #include #include -struct StreamVideoConfig { +enum class LiveVideoProfile { Automatic, H264ConstrainedBaseline, H265Main }; +struct LiveVideoConfig { int width = 1280, height = 720, fps = 30, bitrate = 2500000, codec = 1, rotation = 0; - bool operator==(const StreamVideoConfig&) const = default; + LiveVideoProfile profile = LiveVideoProfile::Automatic; + int level = 0, references = 0, vbvBits = 0; // level: AVC level_idc / HEVC general_level_idc; 0 — auto. + QString backend; // Пусто — проба доступных; непусто — только уже проверенный encoder. + bool operator==(const LiveVideoConfig&) const = default; }; -struct StreamEncodedFrame { QByteArray data; uint64_t pts = 0; bool key = false; }; -class StreamVideoEncoder { +struct LiveEncodedFrame { QByteArray data; uint64_t pts = 0; bool key = false; }; +class LiveVideoEncoder { public: - StreamVideoEncoder(); - ~StreamVideoEncoder(); - bool open(const StreamVideoConfig& config); + LiveVideoEncoder(); + ~LiveVideoEncoder(); + bool open(const LiveVideoConfig& config); bool setBitrate(int bitrate); // true — изменение на следующем кадре; false — нужна смена видеоконтекста void close(); - bool encode(const QImage& image, uint64_t pts, bool key, QList& output); + bool encode(const QImage& image, uint64_t pts, bool key, QList& output); QString error() const; QString name() const; static QList available(); // реальная проба открытия и первого KEY; вызывать вне GUI