From 5995119843f57ba68dc8d66cb6c53cd8ec386ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Tue, 23 Jan 2024 18:40:55 +0100 Subject: [PATCH] Fast ratecontrol, could be better --- libavcodec/roqvideoenc.c | 48 +++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c index 976015d918..0fd70e85c4 100644 --- a/libavcodec/roqvideoenc.c +++ b/libavcodec/roqvideoenc.c @@ -894,7 +894,7 @@ static int roq_encode_video(AVCodecContext *avctx) RoqEncContext *const enc = avctx->priv_data; RoqTempData *const tempData = &enc->tmp_data; RoqContext *const roq = &enc->common; - int ret; + int ret, num_fast_qscale_deltas = 0, num_tries = 0; memset(tempData, 0, sizeof(*tempData)); @@ -908,12 +908,16 @@ static int roq_encode_video(AVCodecContext *avctx) } retry_encode: + num_tries++; for (int i = 0; i < roq->width * roq->height / 64; i++) gather_data_for_cel(enc->cel_evals + i, enc); /* Quake 3 can't handle chunks bigger than 65535 bytes */ +#define MAX_LAMBDA_DELTA 3000000 +#define MAX_TRIES 30 +#define MAX_FAST_TRIES 5 if (tempData->mainChunkSize/8 > 65535 && enc->quake3_compat) { - if (enc->lambda > 100000000) { + if (num_tries > MAX_TRIES) { av_log(roq->logctx, AV_LOG_ERROR, "Cannot encode video in Quake compatible form\n"); return AVERROR(EINVAL); } @@ -941,28 +945,40 @@ static int roq_encode_video(AVCodecContext *avctx) int64_t tol = avctx->bit_rate_tolerance; /* tolerance > bit rate, set to 5% of the bit rate */ - if (tol > avctx->bit_rate) + if (!tol) tol = avctx->bit_rate / 20; av_log(roq->logctx, AV_LOG_VERBOSE, - "\nDesired bit rate (%d kbps), " + "Desired bit rate (%d kbps), " + "Current bit rate (%d kbps), " "Bit rate tolerance (%d), " "Frame rate (%d)\n", - (int)avctx->bit_rate, (int)tol, avctx->time_base.den); + (int)avctx->bit_rate / 1000, ftotal / 1000, (int)tol, avctx->time_base.den); if (ftotal > (avctx->bit_rate + tol)) { /* frame is too big - increase qscale */ - if (enc->lambda > 100000000) { - av_log(roq->logctx, AV_LOG_ERROR, "\nCannot encode video at desired bitrate\n"); - return AVERROR(EINVAL); + if (num_tries > MAX_TRIES) { + av_log(roq->logctx, AV_LOG_WARNING, "Cannot encode video at desired bitrate (got %d kbps)\n", ftotal / 1000); + // don't fail hard + goto keepgoing; + } + enc->lambda_delta = enc->lambda_delta <= 0 ? 1 : enc->lambda_delta < MAX_LAMBDA_DELTA/2 ? enc->lambda_delta*2 : MAX_LAMBDA_DELTA; + if (num_fast_qscale_deltas < MAX_FAST_TRIES) { + float ratio; + int fast_lambda_delta; + ratio = powf((float)ftotal / avctx->bit_rate, 2) - 1; // -1 because it's a delta + av_log(roq->logctx, AV_LOG_VERBOSE, "ratio = %f\n", ratio); + fast_lambda_delta = FFMAX(1, FFMIN(enc->lambda * ratio, MAX_LAMBDA_DELTA)); + // sometimes ratio ends up quite small, in which case we fall back to the simple doubling logic + enc->lambda_delta = FFMAX(enc->lambda_delta, fast_lambda_delta); + num_fast_qscale_deltas++; } - enc->lambda_delta = enc->lambda_delta <= 0 ? 1 : enc->lambda_delta < 65536 ? enc->lambda_delta*2 : 65536; enc->last_lambda = enc->lambda; enc->lambda += enc->lambda_delta; av_log(roq->logctx, AV_LOG_INFO, - "\nGenerated a frame too big for desired bit rate (%d kbps), " - "now switching to a bigger qscale value (%d).\n", - ftotal / 1000, (int)enc->lambda); + "Generated a frame too big for desired bit rate (%d kbps), " + "now switching to a bigger qscale value (%d -> %d).\n", + ftotal / 1000, (int)enc->last_lambda, (int)enc->lambda); tempData->mainChunkSize = 0; memset(tempData->used_option, 0, sizeof(tempData->used_option)); memset(tempData->codebooks.usedCB4, 0, @@ -975,12 +991,12 @@ static int roq_encode_video(AVCodecContext *avctx) /* frame is too small - decrease qscale */ if (enc->lambda <= 1) { av_log(roq->logctx, AV_LOG_WARNING, - "\nGenerated a frame too small for desired bit rate (%d kbps), " + "Generated a frame too small for desired bit rate (%d kbps), " "qscale value cannot be lowered any further (%d).\n", ftotal / 1000, (int)enc->lambda); } else if ((enc->lambda - enc->last_lambda) == 1) { av_log(roq->logctx, AV_LOG_WARNING, - "\nCannot find qscale that gives desired bit rate within desired tolerance, " + "Cannot find qscale that gives desired bit rate within desired tolerance, " "using lower bitrate (%d kbps) with higher qscale value (%d).\n", ftotal / 1000, (int)enc->lambda); } else { @@ -992,7 +1008,7 @@ static int roq_encode_video(AVCodecContext *avctx) enc->lambda = enc->last_lambda; //enc->lambda *= (float)(tempData->mainChunkSize * avctx->time_base.den) / avctx->bit_rate; av_log(roq->logctx, AV_LOG_INFO, - "\nGenerated a frame too small for desired bit rate (%d kbps), " + "Generated a frame too small for desired bit rate (%d kbps), " "reverting lambda and using smaller inc on qscale (%d).\n", ftotal / 1000, (int)enc->lambda); } @@ -1007,6 +1023,7 @@ static int roq_encode_video(AVCodecContext *avctx) } } } +keepgoing: write_codebooks(enc); @@ -1200,6 +1217,7 @@ static const AVClass roq_class = { static const FFCodecDefault roq_defaults[] = { { "b", "0" }, + { "bt", "0" }, { NULL }, }; -- 2.39.2