Browse Source

Update live x264 bitrate and exclude encoders without forced key support

master
evgeny 5 days ago
parent
commit
7ad0aa61af
  1. 15
      tools/chatgui/src/stream_publish_engine.cpp
  2. 26
      tools/chatgui/src/stream_video_encoder.cpp
  3. 1
      tools/chatgui/src/stream_video_encoder.h

15
tools/chatgui/src/stream_publish_engine.cpp

@ -406,6 +406,7 @@ void StreamPublishEngine::audioLoop() {
void StreamPublishEngine::videoLoop() {
#ifdef HAVE_STREAM_CAPTURE
std::unique_ptr<StreamVideoEncoder> encoder;
StreamPublishConfig encoding;
QImage lastImage; uint64_t current = 0; uint32_t revision = 0;
int64_t lastPts = -1, lastKey = -1000000, next = 0, report = 0; unsigned count = 0, skipped = 0;
while (m->running) {
@ -416,6 +417,17 @@ void StreamPublishEngine::videoLoop() {
if (m->frameGeneration == generation) { frame = m->frame; capture = m->framePts; m->frame = {}; }
else capture = -1; }
if (generation != current) {
auto comparable = config.video; comparable.bitrate = encoding.video.bitrate;
bool onlyBitrate = encoder && comparable == encoding.video && config.source.kind == encoding.source.kind
&& config.source.id == encoding.source.id && config.source.window == encoding.source.window;
if (onlyBitrate && encoder->setBitrate(config.video.bitrate)) {
current = generation; encoding = config;
{ std::lock_guard<std::mutex> guard(m->lock);
if (m->generation == generation) { m->good = config; m->hasGood = true; m->confirmedGeneration = generation; } }
QMetaObject::invokeMethod(this, [this, generation] {
if (generation == m->generation) { emit configurationChanged(); emit stateChanged(text("Live")); }
});
} else {
auto candidate = std::make_unique<StreamVideoEncoder>();
if (!candidate->open(config.video)) {
QString error = candidate->error();
@ -424,9 +436,10 @@ void StreamPublishEngine::videoLoop() {
m->wake.wait_for(lock, std::chrono::milliseconds(100), [this, generation] { return !m->running || m->generation != generation; });
continue;
}
encoder = std::move(candidate); current = generation; revision++; lastImage = {}; lastKey = -1000000;
encoder = std::move(candidate); current = generation; encoding = config; revision++; lastImage = {}; lastKey = -1000000;
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop video prepared generation=%llu revision=%u encoder=%s",
(unsigned long long)generation, revision, qPrintable(encoder->name()));
}
}
bool screen = config.source.kind != StreamVideoSource::Camera;
if (frame.isValid()) {

26
tools/chatgui/src/stream_video_encoder.cpp

@ -47,7 +47,8 @@ QList<QByteArray> encoders(int codec) {
case STREAM_MEDIA_H264: return {"h264_nvenc", "h264_qsv", "h264_amf", "h264_vaapi", "libx264", "libopenh264"};
case STREAM_MEDIA_H265: return {"hevc_nvenc", "hevc_qsv", "hevc_amf", "hevc_vaapi", "libx265"};
case STREAM_MEDIA_VP9: return {"vp9_qsv", "vp9_vaapi", "libvpx-vp9"};
case STREAM_MEDIA_AV1: return {"av1_nvenc", "av1_qsv", "av1_amf", "av1_vaapi", "libsvtav1", "libaom-av1"};
// FFmpeg/SVT не принимает произвольный KEY при GOP>1; libaom поддерживает нужный live-контракт.
case STREAM_MEDIA_AV1: return {"av1_nvenc", "av1_qsv", "av1_amf", "av1_vaapi", "libaom-av1"};
case STREAM_MEDIA_H266: return {"libvvenc"};
default: return {};
}
@ -120,9 +121,6 @@ bool StreamVideoEncoder::open(const StreamVideoConfig& config) {
} else if (name == "libvpx-vp9" || name == "libaom-av1") {
av_dict_set(&options, "deadline", "realtime", 0); av_dict_set(&options, "cpu-used", "8", 0);
av_dict_set(&options, "lag-in-frames", "0", 0); av_dict_set(&options, "row-mt", "1", 0);
} else if (name == "libsvtav1") {
c->rc_max_rate = 0;
av_dict_set(&options, "preset", "11", 0); av_dict_set(&options, "svtav1-params", "pred-struct=1:lookahead=0:lp=2", 0);
} else if (name.endsWith("_qsv")) {
av_dict_set(&options, "async_depth", "1", 0); av_dict_set(&options, "look_ahead", "0", 0);
} else if (name.endsWith("_vaapi")) {
@ -194,7 +192,8 @@ bool StreamVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QLi
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[] = {canvas.constBits()}; int stride[] = {static_cast<int>(canvas.bytesPerLine())};
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);
}
m->frame->pts = static_cast<int64_t>(pts); m->frame->pict_type = key ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;
@ -250,8 +249,25 @@ bool StreamVideoEncoder::encode(const QImage& image, uint64_t pts, bool key, QLi
QList<int> StreamVideoEncoder::available() {
QList<int> result;
for (int codec = 1; codec <= 5; codec++) {
#ifdef HAVE_FFMPEG
bool installed = false;
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;
if (encoder.open(config)) result.push_back(codec);
}
return result;
}
bool StreamVideoEncoder::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 при подаче следующего кадра.
if (m->codec && m->name == "libx264") {
m->codec->bit_rate = m->codec->rc_max_rate = bitrate; m->codec->rc_buffer_size = bitrate / 4; m->config.bitrate = bitrate;
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop encoder bitrate updated in place name=libx264 bitrate=%d", bitrate); return true;
}
#endif
DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop bitrate needs encoder replacement name=%s", qPrintable(m->name)); return false;
}

1
tools/chatgui/src/stream_video_encoder.h

@ -19,6 +19,7 @@ public:
StreamVideoEncoder();
~StreamVideoEncoder();
bool open(const StreamVideoConfig& config);
bool setBitrate(int bitrate); // true — изменение на следующем кадре; false — нужна смена видеоконтекста
void close();
bool encode(const QImage& image, uint64_t pts, bool key, QList<StreamEncodedFrame>& output);
QString error() const;

Loading…
Cancel
Save