From patchwork Mon Jan 22 19:14:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Victor Luchitz X-Patchwork-Id: 45728 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:120f:b0:199:de12:6fa6 with SMTP id v15csp177098pzf; Mon, 22 Jan 2024 11:14:53 -0800 (PST) X-Google-Smtp-Source: AGHT+IF8CTX/DKuprlA/Xj6VubmQXKt5ODsytASxXhAxkkZmoDdYo6qLOmw8VfUAY1xQ7X0olejX X-Received: by 2002:a05:6512:398c:b0:50e:7bdf:f123 with SMTP id j12-20020a056512398c00b0050e7bdff123mr2330960lfu.135.1705950893502; Mon, 22 Jan 2024 11:14:53 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1705950893; cv=none; d=google.com; s=arc-20160816; b=L1KCp/XYVU1zz4wv0+wpc1QqDaRygSPehxcaBnWJrVuX5cPGKoqtQrtz4DR/oe29f2 RhtxT/Kui/fExaT9F/YxVt8dS8f/mgNPiEgNfwuezOiRRzVhXkx02ah4SmqvlFvOCOpK nSHpqxafMLdY5MlTpogPsgimC0HzmRZ/J8ru3lxsmHMNNMGlEFTB4oQ6hVbVt+Gb3kjZ joLQk5cfQ/lYeEJS6I9zP4OYTW4Fg14YmnkBMKQmU04yLYwu1o2BOY8NN9V2ufFLsblm z7/QvLnomnukXxuioBa7X0HuczWd52Gw5V3BC7akc3lZdux3mmYlfa+NZhTWEMuaSNhr 89bw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:content-language:to:from:user-agent:mime-version :date:message-id:dkim-signature:delivered-to; bh=emXTn245CbZNU2RIr+aKreBSs6yTyqcqpGJ2/93BwSw=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=zQdYkpVJKIHHit9TPCKllEo5sXHhFJmvTVR5DBKf8NdrmqlfNsQM9skTCbCrol9/i/ 5pHs5W+3lSITBqtQwfhf1jfsukzXXBymG2CKNcEoqG7xU+FYLQwgz2I/Dq3T+XPcORr5 cxnPJxTRd/0kzg1GTqK7+acDgtscF+7aZQqwyhDRa+iQPp83RVqs3j20F7L86FSkyu9t aobWBVzPz3D1/QuRArMFPKrcYyZ6mTirpCPJUjjzgYsFLcpnF+UONeTH0xpRm/Pmzyx8 p1sqHqrGV3krAaCIfi324M0NWmkBHAUcb36W6op6HgtjH6d3CEUnXKi31IgL+vg9bt+e TaDg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b="F6XkUF/v"; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id qq16-20020a17090720d000b00a2cd76d903asi9513216ejb.96.2024.01.22.11.14.53; Mon, 22 Jan 2024 11:14:53 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b="F6XkUF/v"; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 682BB68D0A0; Mon, 22 Jan 2024 21:14:50 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lf1-f41.google.com (mail-lf1-f41.google.com [209.85.167.41]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7B29C68CACA for ; Mon, 22 Jan 2024 21:14:43 +0200 (EET) Received: by mail-lf1-f41.google.com with SMTP id 2adb3069b0e04-50e6ee8e911so4697140e87.1 for ; Mon, 22 Jan 2024 11:14:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1705950882; x=1706555682; darn=ffmpeg.org; h=content-transfer-encoding:content-language:to:subject:from :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=j9bsnr2Qf4K5J2BpieF8i5N8t7OhQO8StRfhG7pWPe4=; b=F6XkUF/vH8o3BwMQ+hvtyZKCocXHozSdX1LwGc9ycv6F4ydxwn95svf1R/3e/6xZMB G50uQunHrlScL1KWg/sIlunoSKK83vvEsepIRIVqGUMt9+NehtSprIwnv2Ip7akDm4vB 0zCrylfF2ouAjlM5dr1g1+870S5hB5no1dBeSamE63srgY165YMfGBe3G3yTS65df1nN GaBCfb//ZIMSTUhkfDN236jsJMYTKiRPFDgUxRW7SOK1ghMkFCDB8fFdMdeZ40SlAezg MILLHOCT8BOgv1LUDOZz8Atb6IqfA38FBTwqI8ZtBcEt/L+dL7vYM6ZSQ95hXIRVL8tp TJHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1705950882; x=1706555682; h=content-transfer-encoding:content-language:to:subject:from :user-agent:mime-version:date:message-id:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=j9bsnr2Qf4K5J2BpieF8i5N8t7OhQO8StRfhG7pWPe4=; b=qyaKBOUIVbstUtIxfId9ut11ZSZhTGgD4ll+Mp34vGDiv1xcJA1nYntVsjwIivQR6E zLU2WbkNjIzIR2bbvGcjHuX96S699vx7cQTARw6JiL64Kgg8IVxPv34Yx4eGyXg3oPK/ dhEK56Ewu26wqTaN/JBaOgXM8rQimUfxorKECn1+c2jSQ8kIa50RBraOTWS0sWCC87kI Ii6TlzF/kFhjbbzd62POE4Uap1tmwwANZ9wcYOov7ldvt8kTziVaMkLeIWZnAIaKx2Ax hORwj7bUN5mWnsscczql88UhEQIzQKOBSG6WPvAg0KxzUdHLnQk0sjDE1UxvjsZWR53x oZow== X-Gm-Message-State: AOJu0YyVuNH5eq0L6anzwSQUm0qrXypaIkd/Is1ph1J6VKIWyrAYV6nu VRT2dnSL3uyIuguhiu6JnKFfRk6iKlDmZTksomV3795niFCfKFbW3Ei0XaI+KQ4= X-Received: by 2002:a05:6512:3c91:b0:50e:aa46:2a95 with SMTP id h17-20020a0565123c9100b0050eaa462a95mr2291709lfv.18.1705950881425; Mon, 22 Jan 2024 11:14:41 -0800 (PST) Received: from [192.168.1.3] ([176.193.23.254]) by smtp.gmail.com with ESMTPSA id b5-20020a056512060500b0050e6bf65b2asm2100768lfe.288.2024.01.22.11.14.41 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 22 Jan 2024 11:14:41 -0800 (PST) Message-ID: <71dbaff5-6ebe-4be7-a098-09424bb4ca04@gmail.com> Date: Mon, 22 Jan 2024 22:14:40 +0300 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird From: Victor Luchits To: ffmpeg-devel@ffmpeg.org Content-Language: ru Subject: [FFmpeg-devel] [PATCH] liavcodec: add bit-rate support to RoQ video encoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: g1esbumkj+LH The bitrate option (-b:v) can now be used to specify the bit rate of the video stream of the RoQ encoder. Original patch by Joseph Fenton aka Chilly Willy Signed-off-by: Victor Luchits --- Changelog | 1 + libavcodec/roqvideo.h | 1 + libavcodec/roqvideodec.c | 16 ++++++ libavcodec/roqvideoenc.c | 107 +++++++++++++++++++++++++++++++++++---- libavcodec/version.h | 2 +- 5 files changed, 117 insertions(+), 10 deletions(-) diff --git a/Changelog b/Changelog index c40b6d08fd..6974312f9d 100644 --- a/Changelog +++ b/Changelog @@ -22,6 +22,7 @@ version : - ffmpeg CLI -bsf option may now be used for input as well as output - ffmpeg CLI options may now be used as -/opt , which is equivalent to -opt > +- RoQ video bit rate option support version 6.1: - libaribcaption decoder diff --git a/libavcodec/roqvideo.h b/libavcodec/roqvideo.h index 2c2e42884d..6d30bcaada 100644 --- a/libavcodec/roqvideo.h +++ b/libavcodec/roqvideo.h @@ -43,6 +43,7 @@ typedef struct RoqContext { AVFrame *last_frame; AVFrame *current_frame; int width, height; + int key_frame; roq_cell cb2x2[256]; roq_qcell cb4x4[256]; diff --git a/libavcodec/roqvideodec.c b/libavcodec/roqvideodec.c index bfc69a65c9..b4ade3a43b 100644 --- a/libavcodec/roqvideodec.c +++ b/libavcodec/roqvideodec.c @@ -70,6 +70,7 @@ static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb) chunk_start = bytestream2_tell(gb); xpos = ypos = 0; + ri->key_frame = 1; if (chunk_size > bytestream2_get_bytes_left(gb)) { av_log(ri->logctx, AV_LOG_ERROR, "Chunk does not fit in input buffer\n"); @@ -92,12 +93,14 @@ static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb) switch(vqid) { case RoQ_ID_MOT: + ri->key_frame = 0; break; case RoQ_ID_FCC: { int byte = bytestream2_get_byte(gb); mx = 8 - (byte >> 4) - ((signed char) (chunk_arg >> 8)); my = 8 - (byte & 0xf) - ((signed char) chunk_arg); ff_apply_motion_8x8(ri, xp, yp, mx, my); + ri->key_frame = 0; break; } case RoQ_ID_SLD: @@ -125,12 +128,14 @@ static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb) vqflg_pos--; switch(vqid) { case RoQ_ID_MOT: + ri->key_frame = 0; break; case RoQ_ID_FCC: { int byte = bytestream2_get_byte(gb); mx = 8 - (byte >> 4) - ((signed char) (chunk_arg >> 8)); my = 8 - (byte & 0xf) - ((signed char) chunk_arg); ff_apply_motion_4x4(ri, x, y, mx, my); + ri->key_frame = 0; break; } case RoQ_ID_SLD: @@ -214,6 +219,17 @@ static int roq_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = av_frame_ref(rframe, s->current_frame)) < 0) return ret; + + /* Keyframe when no MOT or FCC codes in frame */ + if (s->key_frame) { + av_log(avctx, AV_LOG_VERBOSE, "\nFound keyframe!\n"); + rframe->pict_type = AV_PICTURE_TYPE_I; + avpkt->flags |= AV_PKT_FLAG_KEY; + } else { + rframe->pict_type = AV_PICTURE_TYPE_P; + avpkt->flags &= ~AV_PKT_FLAG_KEY; + } + *got_frame = 1; /* shuffle frames */ diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c index 0933abf4f9..68ec9ec238 100644 --- a/libavcodec/roqvideoenc.c +++ b/libavcodec/roqvideoenc.c @@ -136,6 +136,8 @@ typedef struct RoqEncContext { struct ELBGContext *elbg; AVLFG randctx; uint64_t lambda; + uint64_t last_lambda; + int lambda_delta; motion_vect *this_motion4; motion_vect *last_motion4; @@ -887,8 +889,9 @@ static int generate_new_codebooks(RoqEncContext *enc) return 0; } -static int roq_encode_video(RoqEncContext *enc) +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; @@ -910,14 +913,14 @@ static int roq_encode_video(RoqEncContext *enc) /* Quake 3 can't handle chunks bigger than 65535 bytes */ if (tempData->mainChunkSize/8 > 65535 && enc->quake3_compat) { - if (enc->lambda > 100000) { + if (enc->lambda > 100000000) { av_log(roq->logctx, AV_LOG_ERROR, "Cannot encode video in Quake compatible form\n"); return AVERROR(EINVAL); } av_log(roq->logctx, AV_LOG_ERROR, "Warning, generated a frame too big for Quake (%d > 65535), " - "now switching to a bigger qscale value.\n", - tempData->mainChunkSize/8); + "now switching to a bigger qscale value (%d).\n", + tempData->mainChunkSize/8, (int)enc->lambda); enc->lambda *= 1.5; tempData->mainChunkSize = 0; memset(tempData->used_option, 0, sizeof(tempData->used_option)); @@ -931,6 +934,80 @@ static int roq_encode_video(RoqEncContext *enc) remap_codebooks(enc); + /* bit rate control code - could make encoding very slow */ + if (avctx->bit_rate) { + /* a bit rate has been specified - try to match it */ + int ftotal = (tempData->mainChunkSize / 8 + tempData->numCB2*6 + tempData->numCB4*4) * avctx->time_base.den * 8; + int64_t tol = avctx->bit_rate_tolerance; + + /* tolerance > bit rate, set to 5% of the bit rate */ + if (tol > avctx->bit_rate) + tol = avctx->bit_rate / 20; + + av_log(roq->logctx, AV_LOG_VERBOSE, + "\nDesired bit rate (%d kbps), " + "Bit rate tolerance (%d), " + "Frame rate (%d)\n", + (int)avctx->bit_rate, (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); + } + 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); + tempData->mainChunkSize = 0; + memset(tempData->used_option, 0, sizeof(tempData->used_option)); + memset(tempData->codebooks.usedCB4, 0, + sizeof(tempData->codebooks.usedCB4)); + memset(tempData->codebooks.usedCB2, 0, + sizeof(tempData->codebooks.usedCB2)); + + goto retry_encode; + } else if (ftotal < (avctx->bit_rate - tol)) { + /* 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), " + "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, " + "using lower bitrate (%d kbps) with higher qscale value (%d).\n", + ftotal / 1000, (int)enc->lambda); + } else { + enc->lambda_delta = 0; + if (enc->lambda == enc->last_lambda) { + enc->lambda >>= 1; + enc->last_lambda = enc->lambda; + } else { + 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), " + "reverting lambda and using smaller inc on qscale (%d).\n", + ftotal / 1000, (int)enc->lambda); + } + tempData->mainChunkSize = 0; + memset(tempData->used_option, 0, sizeof(tempData->used_option)); + memset(tempData->codebooks.usedCB4, 0, + sizeof(tempData->codebooks.usedCB4)); + memset(tempData->codebooks.usedCB2, 0, + sizeof(tempData->codebooks.usedCB2)); + + goto retry_encode; + } + } + } + write_codebooks(enc); reconstruct_and_encode_image(enc, roq->width, roq->height, @@ -991,8 +1068,11 @@ static av_cold int roq_encode_init(AVCodecContext *avctx) roq->width = avctx->width; roq->height = avctx->height; + enc->lambda = 2*ROQ_LAMBDA_SCALE; enc->framesSinceKeyframe = 0; enc->first_frame = 1; + enc->last_lambda = 1; + enc->lambda_delta = 0; roq->last_frame = av_frame_alloc(); roq->current_frame = av_frame_alloc(); @@ -1059,10 +1139,13 @@ static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, enc->frame_to_enc = frame; - if (frame->quality) - enc->lambda = frame->quality - 1; - else - enc->lambda = 2*ROQ_LAMBDA_SCALE; + if (avctx->bit_rate) { + /* no specific bit rate desired, use frame quality */ + if (frame->quality) + enc->lambda = frame->quality - 1; + else + enc->lambda = 2*ROQ_LAMBDA_SCALE; + } /* 138 bits max per 8x8 block + * 256 codebooks*(6 bytes 2x2 + 4 bytes 4x4) + 8 bytes frame header */ @@ -1089,7 +1172,7 @@ static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, } /* Encode the actual frame */ - ret = roq_encode_video(enc); + ret = roq_encode_video(avctx); if (ret < 0) return ret; @@ -1115,6 +1198,11 @@ static const AVClass roq_class = { .version = LIBAVUTIL_VERSION_INT, }; +static const FFCodecDefault roq_defaults[] = { + { "b", "0" }, + { NULL }, +}; + const FFCodec ff_roq_encoder = { .p.name = "roqvideo", CODEC_LONG_NAME("id RoQ video"), @@ -1129,4 +1217,5 @@ const FFCodec ff_roq_encoder = { AV_PIX_FMT_NONE }, .p.priv_class = &roq_class, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .defaults = roq_defaults, }; diff --git a/libavcodec/version.h b/libavcodec/version.h index 376388c5bb..581151cda7 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -30,7 +30,7 @@ #include "version_major.h" #define LIBAVCODEC_VERSION_MINOR 37 -#define LIBAVCODEC_VERSION_MICRO 100 +#define LIBAVCODEC_VERSION_MICRO 101 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \