From: Michael Niedermayer <michael@niedermayer.cc> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Subject: [FFmpeg-devel] [PATCH 1/2] libavcodec/ffv1: Support storing decorrelated LSB raw without rangecoder Date: Fri, 7 Mar 2025 01:36:02 +0100 Message-ID: <20250307003603.962753-1-michael@niedermayer.cc> (raw) With 16bit float rawlsb 2 gives 0.66% better compression. This is maybe due to the quantization tables being tuned to smaller number of bits rawlsb 4 is about 30% faster than 0 and about 1% worse compression Above was tested using ACES_OT_VWG_SampleFrames Signed-off-by: Michael Niedermayer <michael@niedermayer.cc> --- libavcodec/ffv1.h | 4 ++++ libavcodec/ffv1_template.c | 19 ++++++++++--------- libavcodec/ffv1dec.c | 16 ++++++++++++++-- libavcodec/ffv1dec_template.c | 20 ++++++++++++++++---- libavcodec/ffv1enc.c | 30 ++++++++++++++++++++++++++++-- libavcodec/ffv1enc_template.c | 22 +++++++++++++++++++--- 6 files changed, 91 insertions(+), 20 deletions(-) diff --git a/libavcodec/ffv1.h b/libavcodec/ffv1.h index c23d64d54a4..189004f7981 100644 --- a/libavcodec/ffv1.h +++ b/libavcodec/ffv1.h @@ -85,10 +85,13 @@ typedef struct FFV1SliceContext { int slice_rct_by_coef; int slice_rct_ry_coef; int remap; + int rawlsb; // RefStruct reference, array of MAX_PLANES elements PlaneContext *plane; PutBitContext pb; + PutBitContext rawlsb_pb; + GetBitContext rawlsb_gb; RangeCoder c; int ac_byte_count; ///< number of bytes used for AC coding @@ -146,6 +149,7 @@ typedef struct FFV1Context { int key_frame_ok; int context_model; int qtable; + int rawlsb; int bits_per_raw_sample; int packed_at_lsb; diff --git a/libavcodec/ffv1_template.c b/libavcodec/ffv1_template.c index abb90a12e49..10206702ee8 100644 --- a/libavcodec/ffv1_template.c +++ b/libavcodec/ffv1_template.c @@ -30,24 +30,25 @@ static inline int RENAME(predict)(TYPE *src, TYPE *last) } static inline int RENAME(get_context)(const int16_t quant_table[MAX_CONTEXT_INPUTS][MAX_QUANT_TABLE_SIZE], - TYPE *src, TYPE *last, TYPE *last2) + TYPE *src, TYPE *last, TYPE *last2, int rawlsb) { const int LT = last[-1]; const int T = last[0]; const int RT = last[1]; const int L = src[-1]; + const int rawoff = (1<<rawlsb) >> 1; if (quant_table[3][127] || quant_table[4][127]) { const int TT = last2[0]; const int LL = src[-2]; - return quant_table[0][(L - LT) & MAX_QUANT_TABLE_MASK] + - quant_table[1][(LT - T) & MAX_QUANT_TABLE_MASK] + - quant_table[2][(T - RT) & MAX_QUANT_TABLE_MASK] + - quant_table[3][(LL - L) & MAX_QUANT_TABLE_MASK] + - quant_table[4][(TT - T) & MAX_QUANT_TABLE_MASK]; + return quant_table[0][(L - LT + rawoff >> rawlsb) & MAX_QUANT_TABLE_MASK] + + quant_table[1][(LT - T + rawoff >> rawlsb) & MAX_QUANT_TABLE_MASK] + + quant_table[2][(T - RT + rawoff >> rawlsb) & MAX_QUANT_TABLE_MASK] + + quant_table[3][(LL - L + rawoff >> rawlsb) & MAX_QUANT_TABLE_MASK] + + quant_table[4][(TT - T + rawoff >> rawlsb) & MAX_QUANT_TABLE_MASK]; } else - return quant_table[0][(L - LT) & MAX_QUANT_TABLE_MASK] + - quant_table[1][(LT - T) & MAX_QUANT_TABLE_MASK] + - quant_table[2][(T - RT) & MAX_QUANT_TABLE_MASK]; + return quant_table[0][(L - LT + rawoff >> rawlsb) & MAX_QUANT_TABLE_MASK] + + quant_table[1][(LT - T + rawoff >> rawlsb) & MAX_QUANT_TABLE_MASK] + + quant_table[2][(T - RT + rawoff >> rawlsb) & MAX_QUANT_TABLE_MASK]; } diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c index 3926659ebc9..0f2956eabf7 100644 --- a/libavcodec/ffv1dec.c +++ b/libavcodec/ffv1dec.c @@ -227,6 +227,7 @@ static int decode_slice_header(const FFV1Context *f, av_log(f->avctx, AV_LOG_ERROR, "unsupported remap %d\n", sc->remap); return AVERROR_INVALIDDATA; } + sc->rawlsb = ff_ffv1_get_symbol(c, state, 0); } } @@ -248,6 +249,7 @@ static int decode_slice(AVCodecContext *c, void *arg) FFV1Context *f = c->priv_data; FFV1SliceContext *sc = arg; int width, height, x, y, ret; + int chroma_width, chroma_height; const int ps = av_pix_fmt_desc_get(f->pix_fmt)->comp[0].step; AVFrame * const p = f->picture.f; const int si = sc - f->slices; @@ -284,6 +286,8 @@ static int decode_slice(AVCodecContext *c, void *arg) height = sc->slice_height; x = sc->slice_x; y = sc->slice_y; + chroma_width = AV_CEIL_RSHIFT(width, f->chroma_h_shift); + chroma_height = AV_CEIL_RSHIFT(height, f->chroma_v_shift); if (ac == AC_GOLOMB_RICE) { if (f->combined_version >= 0x30002) @@ -293,11 +297,17 @@ static int decode_slice(AVCodecContext *c, void *arg) sc->c.bytestream_start + sc->ac_byte_count, (sc->c.bytestream_end - sc->c.bytestream_start - sc->ac_byte_count) * 8); } + if (sc->rawlsb) { + int lsb_size = sc->rawlsb * (width * height * (1 + f->transparency) + chroma_width * chroma_height * 2 * f->chroma_planes); + int lsb_size_bytes = (lsb_size + 7) >> 3; + + init_get_bits(&sc->rawlsb_gb, + sc->c.bytestream_end - lsb_size_bytes - 3 - 5*!!f->ec, + lsb_size); + } av_assert1(width && height); if (f->colorspace == 0 && (f->chroma_planes || !f->transparency)) { - const int chroma_width = AV_CEIL_RSHIFT(width, f->chroma_h_shift); - const int chroma_height = AV_CEIL_RSHIFT(height, f->chroma_v_shift); const int cx = x >> f->chroma_h_shift; const int cy = y >> f->chroma_v_shift; decode_plane(f, sc, &gb, p->data[0] + ps*x + y*p->linesize[0], width, height, p->linesize[0], 0, 1, ac); @@ -328,6 +338,8 @@ static int decode_slice(AVCodecContext *c, void *arg) int v; get_rac(&sc->c, (uint8_t[]) { 129 }); v = sc->c.bytestream_end - sc->c.bytestream - 2 - 5*!!f->ec; + if (sc->rawlsb) + v -= get_bits_count(&sc->rawlsb_gb) + 7 >> 3; if (v) { av_log(f->avctx, AV_LOG_ERROR, "bytestream end mismatching by %d\n", v); slice_set_damaged(f, sc); diff --git a/libavcodec/ffv1dec_template.c b/libavcodec/ffv1dec_template.c index f2c88734fe1..37caacb758d 100644 --- a/libavcodec/ffv1dec_template.c +++ b/libavcodec/ffv1dec_template.c @@ -43,10 +43,12 @@ RENAME(decode_line)(FFV1Context *f, FFV1SliceContext *sc, int i; for (x = 0; x < w; x++) { int v = 0; - for (i=0; i<bits; i++) { + for (i=0; i<bits - sc->rawlsb; i++) { uint8_t state = 128; v += v + get_rac(c, &state); } + if (sc->rawlsb) + v = (v << sc->rawlsb) + get_bits(&sc->rawlsb_gb, sc->rawlsb); sample[1][x] = v; } return 0; @@ -60,8 +62,13 @@ RENAME(decode_line)(FFV1Context *f, FFV1SliceContext *sc, return AVERROR_INVALIDDATA; } - context = RENAME(get_context)(quant_table, - sample[1] + x, sample[0] + x, sample[1] + x); + if (sc->rawlsb) { + context = RENAME(get_context)(quant_table, + sample[1] + x, sample[0] + x, sample[1] + x, sc->rawlsb); + } else { + context = RENAME(get_context)(quant_table, + sample[1] + x, sample[0] + x, sample[1] + x, 0); + } if (context < 0) { context = -context; sign = 1; @@ -71,7 +78,12 @@ RENAME(decode_line)(FFV1Context *f, FFV1SliceContext *sc, av_assert2(context < p->context_count); if (ac != AC_GOLOMB_RICE) { - diff = get_symbol_inline(c, p->state[context], 1); + if (sc->rawlsb) { + const int rawoff = (1<<sc->rawlsb) >> 1; + diff = get_bits(&sc->rawlsb_gb, sc->rawlsb); + diff += (get_symbol_inline(c, p->state[context], 1) << sc->rawlsb) - rawoff; + } else + diff = get_symbol_inline(c, p->state[context], 1); } else { if (context == 0 && run_mode == 0) run_mode = 1; diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c index 1292b4227d3..b4080f29002 100644 --- a/libavcodec/ffv1enc.c +++ b/libavcodec/ffv1enc.c @@ -594,6 +594,9 @@ av_cold int ff_ffv1_encode_init(AVCodecContext *avctx) if (s->ec == 2) s->version = FFMAX(s->version, 4); + if (s->rawlsb) + s->version = FFMAX(s->version, 4); + if ((s->version == 2 || s->version>3) && avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { av_log(avctx, AV_LOG_ERROR, "Version 2 or 4 needed for requested features but version 2 or 4 is experimental and not enabled\n"); return AVERROR_INVALIDDATA; @@ -920,6 +923,11 @@ static int encode_init_internal(AVCodecContext *avctx) } } + if (s->rawlsb > s->bits_per_raw_sample) { + av_log(avctx, AV_LOG_ERROR, "too many raw lsb\n"); + return AVERROR(EINVAL); + } + ret = ff_ffv1_encode_init(avctx); if (ret < 0) @@ -1003,6 +1011,7 @@ static void encode_slice_header(FFV1Context *f, FFV1SliceContext *sc) put_symbol(c, state, sc->slice_rct_ry_coef, 0); } put_symbol(c, state, sc->remap, 0); + put_symbol(c, state, sc->rawlsb, 0); } } @@ -1113,6 +1122,8 @@ static int encode_slice(AVCodecContext *c, void *arg) const int ps = av_pix_fmt_desc_get(c->pix_fmt)->comp[0].step; int ret; RangeCoder c_bak = sc->c; + const int chroma_width = AV_CEIL_RSHIFT(width, f->chroma_h_shift); + const int chroma_height = AV_CEIL_RSHIFT(height, f->chroma_v_shift); const uint8_t *planes[4] = {p->data[0] + ps*x + y*p->linesize[0], p->data[1] ? p->data[1] + ps*x + y*p->linesize[1] : NULL, p->data[2] ? p->data[2] + ps*x + y*p->linesize[2] : NULL, @@ -1127,6 +1138,8 @@ static int encode_slice(AVCodecContext *c, void *arg) sc->slice_rct_ry_coef = 1; } + sc->rawlsb = f->rawlsb; // we do not optimize this per slice, but other encoders could + retry: if (f->key_frame) ff_ffv1_clear_slice_state(f, sc); @@ -1139,10 +1152,15 @@ retry: sc->c.bytestream_start + sc->ac_byte_count, sc->c.bytestream_end - sc->c.bytestream_start - sc->ac_byte_count); } + if (sc->rawlsb) { + int lsb_size = sc->rawlsb * (width * height * (1 + !!f->transparency) + chroma_width * chroma_height * 2 * f->chroma_planes); + int lsb_size_bytes = (lsb_size + 7) >> 3; + init_put_bits(&sc->rawlsb_pb, + sc->c.bytestream_end - lsb_size_bytes, + lsb_size_bytes); + } if (f->colorspace == 0 && c->pix_fmt != AV_PIX_FMT_YA8) { - const int chroma_width = AV_CEIL_RSHIFT(width, f->chroma_h_shift); - const int chroma_height = AV_CEIL_RSHIFT(height, f->chroma_v_shift); const int cx = x >> f->chroma_h_shift; const int cy = y >> f->chroma_v_shift; @@ -1318,6 +1336,12 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, if (i > 0 || f->version > 2) { av_assert0(bytes < pkt->size / f->slice_count); memmove(buf_p, sc->c.bytestream_start, bytes); + if (sc->rawlsb) { + flush_put_bits(&sc->rawlsb_pb); + av_assert0(bytes + put_bytes_count(&sc->rawlsb_pb, 1) < pkt->size / f->slice_count); + memmove(buf_p + bytes, sc->rawlsb_pb.buf, put_bytes_count(&sc->rawlsb_pb, 1)); + bytes += put_bytes_count(&sc->rawlsb_pb, 1); + } av_assert0(bytes < (1 << 24)); AV_WB24(buf_p + bytes, bytes); bytes += 3; @@ -1377,6 +1401,8 @@ static const AVOption options[] = { { .i64 = QTABLE_8BIT }, INT_MIN, INT_MAX, VE, .unit = "qtable" }, { "greater8bit", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QTABLE_GT8BIT }, INT_MIN, INT_MAX, VE, .unit = "qtable" }, + { "rawlsb", "number of LSBs stored RAW", OFFSET(rawlsb), AV_OPT_TYPE_INT, + { .i64 = 0 }, 0, 8, VE }, { NULL } }; diff --git a/libavcodec/ffv1enc_template.c b/libavcodec/ffv1enc_template.c index af3354497ae..9e1252010dd 100644 --- a/libavcodec/ffv1enc_template.c +++ b/libavcodec/ffv1enc_template.c @@ -34,6 +34,8 @@ RENAME(encode_line)(FFV1Context *f, FFV1SliceContext *sc, int run_index = sc->run_index; int run_count = 0; int run_mode = 0; + const int rawoff = (1<<sc->rawlsb) >> 1; + const unsigned mask = (1<<sc->rawlsb) - 1; if (ac != AC_GOLOMB_RICE) { if (c->bytestream_end - c->bytestream < w * 35) { @@ -51,10 +53,13 @@ RENAME(encode_line)(FFV1Context *f, FFV1SliceContext *sc, for (x = 0; x < w; x++) { int i; int v = sample[0][x]; - for (i = bits-1; i>=0; i--) { + + for (i = bits-1; i>=sc->rawlsb; i--) { uint8_t state = 128; put_rac(c, &state, (v>>i) & 1); } + if (sc->rawlsb) + put_bits(&sc->rawlsb_pb, sc->rawlsb, v & mask); } return 0; } @@ -62,8 +67,14 @@ RENAME(encode_line)(FFV1Context *f, FFV1SliceContext *sc, for (x = 0; x < w; x++) { int diff, context; - context = RENAME(get_context)(f->quant_tables[p->quant_table_index], - sample[0] + x, sample[1] + x, sample[2] + x); + if (sc->rawlsb) { + context = RENAME(get_context)(f->quant_tables[p->quant_table_index], + sample[0] + x, sample[1] + x, sample[2] + x, sc->rawlsb); + } else { + //try to force a version with rawlsb optimized out + context = RENAME(get_context)(f->quant_tables[p->quant_table_index], + sample[0] + x, sample[1] + x, sample[2] + x, 0); + } diff = sample[0][x] - RENAME(predict)(sample[0] + x, sample[1] + x); if (context < 0) { @@ -74,6 +85,11 @@ RENAME(encode_line)(FFV1Context *f, FFV1SliceContext *sc, diff = fold(diff, bits); if (ac != AC_GOLOMB_RICE) { + if (sc->rawlsb) { + diff += rawoff; + put_bits(&sc->rawlsb_pb, sc->rawlsb, diff & mask); + diff = diff >> sc->rawlsb; // Note, this will be biased on small rawlsb + } if (pass1) { put_symbol_inline(c, p->state[context], diff, 1, sc->rc_stat, sc->rc_stat2[p->quant_table_index][context]); -- 2.48.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next reply other threads:[~2025-03-07 0:36 UTC|newest] Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top 2025-03-07 0:36 Michael Niedermayer [this message] 2025-03-07 0:36 ` [FFmpeg-devel] [PATCH 2/2] avcodec/ffv1dec_template: wrap around in fltmap table Michael Niedermayer 2025-03-08 19:36 ` Michael Niedermayer
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20250307003603.962753-1-michael@niedermayer.cc \ --to=michael@niedermayer.cc \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git