fix(codec): set CRF via x264's private option, not global_quality
The CRF value was set through AVCodecContext.global_quality with AV_CODEC_FLAG_QSCALE, which FFmpeg's libx264 wrapper divides by FF_QP2LAMBDA (118) before passing to x264 — turning CRF 16 into CRF 0.135 (essentially lossless) while x264 logged '-qscale is ignored, -crf is recommended' and fell back to its own defaults. The CRF was never actually applied. Now the CRF is set as x264's private "crf" option via av_opt_set, which passes the exact value directly to the encoder. The VBV max rate still caps bursts as before.
This commit is contained in:
@@ -417,7 +417,15 @@ CodecResult<std::unique_ptr<Encoder>> EncoderFactory::create(const EncoderConfig
|
||||
// a fixed bitrate, and let the encoder use fewer bits on static screen
|
||||
// content and more on motion or text. The VBV max rate still caps the
|
||||
// peak so bursts cannot overflow the receiver's UDP buffers.
|
||||
ctx->global_quality = static_cast<int>(config.crf);
|
||||
// CRF rate control: set x264's CRF directly as a private option.
|
||||
// FFmpeg's global_quality + AV_CODEC_FLAG_QSCALE path divides by
|
||||
// FF_QP2LAMBDA, giving a wrong CRF value (16/118 ≈ 0.1, essentially
|
||||
// lossless) — hence the "-qscale is ignored" warning. Setting the
|
||||
// private "crf" option bypasses that and gives x264 the exact value.
|
||||
const std::string crf_value = std::to_string(config.crf);
|
||||
if (av_opt_set(ctx->priv_data, "crf", crf_value.c_str(), 0) < 0) {
|
||||
return CodecError{"failed to set CRF quality"};
|
||||
}
|
||||
ctx->rc_max_rate = static_cast<int64_t>(config.bitrate_kbps) * 1000;
|
||||
// VBV: one frame period of budget keeps bursts tight — a two-frame
|
||||
// buffer lets a keyframe spike beyond what a constrained link can
|
||||
|
||||
Reference in New Issue
Block a user