Apply configured QP ceilings and expose live encoder adjustment capability
This commit is contained in:
1 parent
54b50f4177
commit
0070221045
2 files changed
+37
-2
No files matched your search
@@ -141,6 +141,7 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config, Backend backend) {
|
|||||||
|| config.width % 2 || config.height % 2 || config.fps < 1 || config.fps > 60
|
|| config.width % 2 || config.height % 2 || config.fps < 1 || config.fps > 60
|
||||||
|| config.bitrate < 64000 || config.bitrate > 16000000 || config.codec < 1 || config.codec > 5
|
|| config.bitrate < 64000 || config.bitrate > 16000000 || config.codec < 1 || config.codec > 5
|
||||||
|| config.vbvBits < 0 || config.vbvBits > 16000000 || config.references < 0 || config.references > 16
|
|| config.vbvBits < 0 || config.vbvBits > 16000000 || config.references < 0 || config.references > 16
|
||||||
|
|| config.maxQp < -1 || config.maxQp > 51 || (config.maxQp >= 0 && (constrained || config.codec > STREAM_MEDIA_H265))
|
||||||
|| (constrained && (config.level <= 0 || config.level > 255 || config.references != 1 || !config.vbvBits
|
|| (constrained && (config.level <= 0 || config.level > 255 || config.references != 1 || !config.vbvBits
|
||||||
|| !((config.codec == 1 && config.profile == LiveVideoProfile::H264ConstrainedBaseline)
|
|| !((config.codec == 1 && config.profile == LiveVideoProfile::H264ConstrainedBaseline)
|
||||||
|| (config.codec == 2 && config.profile == LiveVideoProfile::H265Main))))) {
|
|| (config.codec == 2 && config.profile == LiveVideoProfile::H265Main))))) {
|
||||||
@@ -162,6 +163,7 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config, Backend backend) {
|
|||||||
c->width = config.width; c->height = config.height; c->time_base = {1, 1000000}; c->framerate = {config.fps, 1};
|
c->width = config.width; c->height = config.height; c->time_base = {1, 1000000}; c->framerate = {config.fps, 1};
|
||||||
if (constrained && name.endsWith("_qsv")) c->time_base = {1, 90000};
|
if (constrained && name.endsWith("_qsv")) c->time_base = {1, 90000};
|
||||||
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; }
|
||||||
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 = 2;
|
||||||
if (config.references) c->refs = config.references;
|
if (config.references) c->refs = config.references;
|
||||||
@@ -356,9 +358,10 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config, Backend backend) {
|
|||||||
c->qmin, c->qmax, c->time_base.num, c->time_base.den, c->framerate.num, c->framerate.den);
|
c->qmin, c->qmax, c->time_base.num, c->time_base.den, c->framerate.num, c->framerate.den);
|
||||||
}
|
}
|
||||||
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 forced_keys=%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",
|
||||||
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.level, config.references, c->rc_buffer_size, constrained, m->forcedKeys, config.maxQp, canSetMaxQp());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -569,3 +572,33 @@ bool LiveVideoEncoder::setBitrate(int bitrate) {
|
|||||||
#endif
|
#endif
|
||||||
DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop bitrate needs encoder replacement name=%s", qPrintable(m->name)); return false;
|
DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "desktop bitrate needs encoder replacement name=%s", qPrintable(m->name)); return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LiveVideoEncoder::canSetMaxQp() const {
|
||||||
|
#ifdef HAVE_FFMPEG
|
||||||
|
if (!m->codec || m->config.profile != LiveVideoProfile::Automatic || m->config.codec > STREAM_MEDIA_H265) return false;
|
||||||
|
return m->name.endsWith("_qsv") || (m->name.endsWith("_amf")
|
||||||
|
&& av_opt_find(m->codec->priv_data, "utun_qp_max", nullptr, 0, 0));
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LiveVideoEncoder::setMaxQp(int qp) {
|
||||||
|
if (qp < 0 || qp > 51 || !canSetMaxQp()) {
|
||||||
|
m->error = "Live QP adjustment is unavailable";
|
||||||
|
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop live QP ceiling unavailable name=%s qp=%d", qPrintable(m->name), qp); return false;
|
||||||
|
}
|
||||||
|
#ifdef HAVE_FFMPEG
|
||||||
|
int result = m->name.endsWith("_amf") ? av_opt_set_int(m->codec->priv_data, "utun_qp_max", qp, 0) : 0;
|
||||||
|
if (result < 0) {
|
||||||
|
m->error = ffError(result);
|
||||||
|
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop live QP ceiling failed name=%s qp=%d error=%s",
|
||||||
|
qPrintable(m->name), qp, qPrintable(m->error)); return false;
|
||||||
|
}
|
||||||
|
m->codec->qmin = 0; m->codec->qmax = qp;
|
||||||
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "desktop live QP ceiling requested name=%s qp=%d", qPrintable(m->name), qp);
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ struct StreamPublishConfig {
|
|||||||
bool audio = true;
|
bool audio = true;
|
||||||
bool showCursor = true;
|
bool showCursor = true;
|
||||||
bool nativeSize = false, automaticBitrate = false;
|
bool nativeSize = false, automaticBitrate = false;
|
||||||
|
bool adaptiveQp = false; // При перерасходе битрейта плавно повышать потолок выше video.maxQp, затем возвращать.
|
||||||
};
|
};
|
||||||
class StreamPublishEngine : public QObject {
|
class StreamPublishEngine : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -50,6 +51,7 @@ public:
|
|||||||
uint64_t group() const;
|
uint64_t group() const;
|
||||||
StreamPublishConfig config() const;
|
StreamPublishConfig config() const;
|
||||||
uint64_t videoBitrate() const; // Фактический выход video encoder за последний интервал около секунды, бит/с; читать в GUI-потоке.
|
uint64_t videoBitrate() const; // Фактический выход video encoder за последний интервал около секунды, бит/с; читать в GUI-потоке.
|
||||||
|
int videoMaxQp() const; // Текущий потолок QP с учётом адаптации; -1 — настройки encoder.
|
||||||
QVariant previewFrame() const;
|
QVariant previewFrame() const;
|
||||||
QList<int> codecs() const;
|
QList<int> codecs() const;
|
||||||
static QList<StreamVideoSource> sources();
|
static QList<StreamVideoSource> sources();
|
||||||
|
|||||||
Reference in new issue
Block a user