Use four CPU encoder workers and retain GPU priority with software fallback

This commit is contained in:
evgeny committed 2026-10-19 19:04:08 +02:00
1 parent 28f3b264b8
commit dc22014bcc
3 files changed
+11 -12

No files matched your search

+1 -1
View File
@@ -17,7 +17,7 @@ bool CallVideoEncoder::open(const call_video_format& format, uint32_t bitrate, c
config.bitrate = bitrate; config.references = 1; config.backend = backend; config.bitrate = bitrate; config.references = 1; config.backend = backend;
config.profile = format.codec == CALL_VIDEO_H264 ? LiveVideoProfile::H264ConstrainedBaseline : LiveVideoProfile::H265Main; config.profile = format.codec == CALL_VIDEO_H264 ? LiveVideoProfile::H264ConstrainedBaseline : LiveVideoProfile::H265Main;
auto candidate = std::make_unique<LiveVideoEncoder>(); auto candidate = std::make_unique<LiveVideoEncoder>();
if (!candidate->open(config, backend.isEmpty() ? LiveVideoEncoder::Backend::HardwareOnly : LiveVideoEncoder::Backend::Automatic)) { if (!candidate->open(config, LiveVideoEncoder::Backend::Automatic)) {
m_error = candidate->error(); DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call video open failed: %s", qPrintable(m_error)); return false; m_error = candidate->error(); DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call video open failed: %s", qPrintable(m_error)); return false;
} }
close(); m_encoder = std::move(candidate); m_config = config; m_config.backend = m_encoder->name(); m_format = format; close(); m_encoder = std::move(candidate); m_config = config; m_config.backend = m_encoder->name(); m_format = format;
+2 -2
View File
@@ -1,7 +1,7 @@
/* Кодер видеотрека звонка; только video worker, без камеры, транспорта и таймеров. /* Кодер видеотрека звонка; только video worker, без камеры, транспорта и таймеров.
* Смена bitrate ожидает ближайший IDR (период <= 1с по capture PTS). Новый encoder * Смена bitrate ожидает ближайший IDR (период <= 1с по capture PTS). Новый encoder
* получает поток после полного drain старого; completion выдаётся после проверки нового IDR. Выбирается GPU; * получает поток после полного drain старого; completion выдаётся после проверки нового IDR. Приоритет GPU, затем CPU;
* явный backend позволяет отдельно проверять software в тестах. Completion передаётся в uasync * явный backend фиксирует выбор для теста/замены. Completion передаётся в uasync
* независимо от admission видео. PTS, generation и frame_id принадлежат владельцу. * независимо от admission видео. PTS, generation и frame_id принадлежат владельцу.
* open получает согласованный call_video_format; соответствие level/размера/FPS * open получает согласованный call_video_format; соответствие level/размера/FPS
* проверяет общий модуль, фактический bitstream — LiveVideoEncoder. CAPS проверяет владелец. */ * проверяет общий модуль, фактический bitstream — LiveVideoEncoder. CAPS проверяет владелец. */
+8 -9
View File
@@ -166,7 +166,7 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config, Backend backend) {
c->bit_rate = config.bitrate; c->rc_max_rate = config.bitrate; c->bit_rate = config.bitrate; c->rc_max_rate = config.bitrate;
if (config.maxQp >= 0) { c->qmin = 0; c->qmax = config.maxQp; } if (config.maxQp >= 0) { c->qmin = 0; c->qmax = config.maxQp; }
c->rc_buffer_size = config.vbvBits ? config.vbvBits : config.bitrate / 4; c->rc_buffer_size = config.vbvBits ? config.vbvBits : config.bitrate / 4;
c->gop_size = config.fps; c->max_b_frames = 0; c->thread_count = 2; c->gop_size = config.fps; c->max_b_frames = 0; c->thread_count = 4;
if (config.references) c->refs = config.references; if (config.references) c->refs = config.references;
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER | AV_CODEC_FLAG_CLOSED_GOP; c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER | AV_CODEC_FLAG_CLOSED_GOP;
if (constrained) { if (constrained) {
@@ -195,14 +195,12 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config, Backend backend) {
if (name == "libx264" || name == "libx265") { if (name == "libx264" || name == "libx265") {
av_dict_set(&options, "preset", "ultrafast", 0); av_dict_set(&options, "tune", "zerolatency", 0); av_dict_set(&options, "preset", "ultrafast", 0); av_dict_set(&options, "tune", "zerolatency", 0);
av_dict_set(&options, "forced-idr", "1", 0); av_dict_set(&options, "forced-idr", "1", 0);
const int workers = std::clamp(av_cpu_count(), 1, 4); const int workers = 4;
QByteArray params = name == "libx264" ? "scenecut=0:open-gop=0:repeat-headers=0" QByteArray params = name == "libx264" ? "threads=4:sliced-threads=1:scenecut=0:open-gop=0:repeat-headers=0"
: "bframes=0:rc-lookahead=0:open-gop=0:frame-threads=1:wpp=1:log-level=error:pools=" + QByteArray::number(workers); : "bframes=0:rc-lookahead=0:open-gop=0:frame-threads=1:wpp=1:log-level=error:pools=" + QByteArray::number(workers);
if (name == "libx265") DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "live CPU parallelism encoder=%s workers=%d mode=%s cpu_count=%d",
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "live HEVC parallelism workers=%d frame_threads=1 wpp=1 cpu_count=%d", name.constData(), workers, name == "libx264" ? "sliced" : "wpp/frame-threads=1", av_cpu_count());
workers, av_cpu_count());
if (constrained) { if (constrained) {
c->thread_count = 1;
av_dict_set(&options, "profile", name == "libx264" ? "baseline" : "main", 0); av_dict_set(&options, "profile", name == "libx264" ? "baseline" : "main", 0);
// Оба encoder принимают level × 10 целым: дробный разбор зависит от LC_NUMERIC. // Оба encoder принимают level × 10 целым: дробный разбор зависит от LC_NUMERIC.
QByteArray level = QByteArray::number(name == "libx264" ? config.level : config.level / 3); QByteArray level = QByteArray::number(name == "libx264" ? config.level : config.level / 3);
@@ -360,9 +358,10 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config, Backend backend) {
} }
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, DEBUG_INFO(DEBUG_CATEGORY_VIDEO,
"desktop encoder ready name=%s codec=%d size=%dx%d fps=%d bitrate=%d level=%d refs=%d vbv=%d profile_verified=%d " "desktop encoder ready name=%s codec=%d size=%dx%d fps=%d bitrate=%d level=%d refs=%d vbv=%d profile_verified=%d "
"forced_keys=%d max_qp=%d dynamic_qp=%d", "forced_keys=%d max_qp=%d dynamic_qp=%d threads=%d thread_type=%d",
name.constData(), config.codec, config.width, config.height, config.fps, config.bitrate, name.constData(), config.codec, config.width, config.height, config.fps, config.bitrate,
config.level, config.references, c->rc_buffer_size, constrained, m->forcedKeys, config.maxQp, canSetMaxQp()); config.level, config.references, c->rc_buffer_size, constrained, m->forcedKeys, config.maxQp, canSetMaxQp(),
c->thread_count, c->active_thread_type);
return true; return true;
} }
} }