From: Michael Niedermayer <michael@niedermayer.cc> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Subject: [FFmpeg-devel] [PATCH v2] avcodec/ffv1: Store slices*planes with the minimum bits needed after remap Date: Thu, 3 Apr 2025 02:36:36 +0200 Message-ID: <20250403003636.106191-1-michael@niedermayer.cc> (raw) This also means that if a plane*slice has only 1 color nothing is stored after the remap table This improves compression, but more tests are needed This also corrects the RCT offset to the exact value after remap not a fixed 65536 Signed-off-by: Michael Niedermayer <michael@niedermayer.cc> --- libavcodec/ffv1.c | 37 +++++++++++++++++++++++++++++++++++ libavcodec/ffv1.h | 2 ++ libavcodec/ffv1dec.c | 17 +++++++++++++--- libavcodec/ffv1dec_template.c | 32 ++++++++++++++++++------------ libavcodec/ffv1enc.c | 15 +++++++++----- libavcodec/ffv1enc_template.c | 12 ++++++++---- 6 files changed, 91 insertions(+), 24 deletions(-) diff --git a/libavcodec/ffv1.c b/libavcodec/ffv1.c index cff16c5cc76..86fe9455c4c 100644 --- a/libavcodec/ffv1.c +++ b/libavcodec/ffv1.c @@ -219,6 +219,43 @@ void ff_ffv1_clear_slice_state(const FFV1Context *f, FFV1SliceContext *sc) } } +void ff_ffv1_compute_bits_per_plane(const FFV1Context *f, FFV1SliceContext *sc, int bits[4], int *offset, int mask[4], int bits_per_raw_sample) +{ + // to simplify we use the remap_count as the symbol range in each plane + if (!sc->remap) { + sc->remap_count[0] = + sc->remap_count[1] = + sc->remap_count[2] = + sc->remap_count[3] = 1 << (bits_per_raw_sample > 0 ? bits_per_raw_sample : 8); + } + + if (sc->remap) + av_assert0(bits_per_raw_sample > 8); //breaks with lbd, needs review if added + + //bits with no RCT + for (int p=0; p<3+f->transparency; p++) { + bits[p] = av_ceil_log2(sc->remap_count[p]); + if (mask) + mask[p] = (1<<bits[p]) - 1; + } + + //RCT + if (sc->slice_coding_mode == 0) { + *offset = sc->remap_count[0]; + + bits[0] = av_ceil_log2(FFMAX3(sc->remap_count[0], sc->remap_count[1], sc->remap_count[2])); + bits[1] = av_ceil_log2(sc->remap_count[0] + sc->remap_count[1]); + bits[2] = av_ceil_log2(sc->remap_count[0] + sc->remap_count[2]); + + //old version coded a bit more than needed + if (f->combined_version < 0x40008) { + bits[0]++; + if(f->transparency) + bits[3]++; + } + } +} + int ff_ffv1_get_symbol(RangeCoder *c, uint8_t *state, int is_signed) { return get_symbol_inline(c, state, is_signed); diff --git a/libavcodec/ffv1.h b/libavcodec/ffv1.h index 24cbb694d41..c95508e2844 100644 --- a/libavcodec/ffv1.h +++ b/libavcodec/ffv1.h @@ -106,6 +106,7 @@ typedef struct FFV1SliceContext { uint64_t (*rc_stat2[MAX_QUANT_TABLES])[32][2]; }; }; + int remap_count[4]; uint16_t *bitmap [4]; //float encode uint16_t *fltmap [4]; //halffloat encode & decode @@ -197,6 +198,7 @@ int ff_ffv1_parse_header(FFV1Context *f, RangeCoder *c, uint8_t *state); int ff_ffv1_read_extra_header(FFV1Context *f); int ff_ffv1_read_quant_tables(RangeCoder *c, int16_t quant_table[MAX_CONTEXT_INPUTS][256]); +void ff_ffv1_compute_bits_per_plane(const FFV1Context *f, FFV1SliceContext *sc, int bits[4], int offset[1], int mask[4], int bits_per_raw_sample); int ff_ffv1_get_symbol(RangeCoder *c, uint8_t *state, int is_signed); /** diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c index ff7935ed722..dc4eeecc87c 100644 --- a/libavcodec/ffv1dec.c +++ b/libavcodec/ffv1dec.c @@ -99,6 +99,16 @@ static int decode_plane(FFV1Context *f, FFV1SliceContext *sc, { int x, y; int16_t *sample[2]; + int bits; + unsigned mask; + + if (sc->remap) { + bits = av_ceil_log2(sc->remap_count[remap_index]); + mask = (1<<bits)-1; + } else { + bits = f->avctx->bits_per_raw_sample; + } + sample[0] = sc->sample_buffer + 3; sample[1] = sc->sample_buffer + w + 6 + 3; @@ -125,18 +135,18 @@ static int decode_plane(FFV1Context *f, FFV1SliceContext *sc, for (x = 0; x < w; x++) src[x*pixel_stride + stride * y] = sample[1][x]; } else { - int ret = decode_line(f, sc, gb, w, sample, plane_index, f->avctx->bits_per_raw_sample, ac); + int ret = decode_line(f, sc, gb, w, sample, plane_index, bits, ac); if (ret < 0) return ret; if (sc->remap) { if (f->packed_at_lsb || f->avctx->bits_per_raw_sample == 16) { for (x = 0; x < w; x++) { - ((uint16_t*)(src + stride*y))[x*pixel_stride] = sc->fltmap[remap_index][sample[1][x] & 0xFFFF]; + ((uint16_t*)(src + stride*y))[x*pixel_stride] = sc->fltmap[remap_index][sample[1][x] & mask]; } } else { for (x = 0; x < w; x++) { - int v = sc->fltmap[remap_index][sample[1][x] & 0xFFFF]; + int v = sc->fltmap[remap_index][sample[1][x] & mask]; ((uint16_t*)(src + stride*y))[x*pixel_stride] = v << (16 - f->avctx->bits_per_raw_sample) | v >> (2 * f->avctx->bits_per_raw_sample - 16); } } @@ -340,6 +350,7 @@ static int decode_remap(FFV1Context *f, FFV1SliceContext *sc) } lu ^= !run; } + sc->remap_count[p] = j; } return 0; } diff --git a/libavcodec/ffv1dec_template.c b/libavcodec/ffv1dec_template.c index 97116a6dc5b..b88195cfba3 100644 --- a/libavcodec/ffv1dec_template.c +++ b/libavcodec/ffv1dec_template.c @@ -36,6 +36,12 @@ RENAME(decode_line)(FFV1Context *f, FFV1SliceContext *sc, int run_mode = 0; int run_index = sc->run_index; + if (bits == 0) { + for (x = 0; x < w; x++) + sample[1][x] = 0; + return 0; + } + if (is_input_end(c, gb, ac)) return AVERROR_INVALIDDATA; @@ -138,10 +144,12 @@ static int RENAME(decode_rgb_frame)(FFV1Context *f, FFV1SliceContext *sc, int x, y, p; TYPE *sample[4][2]; int lbd = f->avctx->bits_per_raw_sample <= 8; - int bits = f->avctx->bits_per_raw_sample > 0 ? FFMIN(f->avctx->bits_per_raw_sample, 16) : 8; - int offset = 1 << bits; + int bits[4], offset; int transparency = f->transparency; int ac = f->ac; + unsigned mask[4]; + + ff_ffv1_compute_bits_per_plane(f, sc, bits, &offset, mask, f->avctx->bits_per_raw_sample); if (sc->slice_coding_mode == 1) ac = 1; @@ -165,10 +173,10 @@ static int RENAME(decode_rgb_frame)(FFV1Context *f, FFV1SliceContext *sc, sample[p][1][-1]= sample[p][0][0 ]; sample[p][0][ w]= sample[p][0][w-1]; - if (lbd && sc->slice_coding_mode == 0) + if (bits[p] == 9) ret = RENAME(decode_line)(f, sc, gb, w, sample[p], (p + 1)/2, 9, ac); else - ret = RENAME(decode_line)(f, sc, gb, w, sample[p], (p + 1)/2, bits + (sc->slice_coding_mode != 1), ac); + ret = RENAME(decode_line)(f, sc, gb, w, sample[p], (p + 1)/2, bits[p], ac); if (ret < 0) return ret; } @@ -187,17 +195,17 @@ static int RENAME(decode_rgb_frame)(FFV1Context *f, FFV1SliceContext *sc, } if (sc->remap) { if (f->avctx->bits_per_raw_sample == 32) { - g = sc->fltmap32[0][g & 0xFFFF]; - b = sc->fltmap32[1][b & 0xFFFF]; - r = sc->fltmap32[2][r & 0xFFFF]; + g = sc->fltmap32[0][g & mask[0]]; + b = sc->fltmap32[1][b & mask[1]]; + r = sc->fltmap32[2][r & mask[2]]; if (transparency) - a = sc->fltmap32[3][a & 0xFFFF]; + a = sc->fltmap32[3][a & mask[3]]; } else { - g = sc->fltmap[0][g & 0xFFFF]; - b = sc->fltmap[1][b & 0xFFFF]; - r = sc->fltmap[2][r & 0xFFFF]; + g = sc->fltmap[0][g & mask[0]]; + b = sc->fltmap[1][b & mask[1]]; + r = sc->fltmap[2][r & mask[2]]; if (transparency) - a = sc->fltmap[3][a & 0xFFFF]; + a = sc->fltmap[3][a & mask[3]]; } } diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c index b704cec9f3a..8f61b183eb0 100644 --- a/libavcodec/ffv1enc.c +++ b/libavcodec/ffv1enc.c @@ -433,7 +433,7 @@ static void set_micro_version(FFV1Context *f) if (f->version == 3) { f->micro_version = 4; } else if (f->version == 4) { - f->micro_version = 7; + f->micro_version = 8; } else av_assert0(0); @@ -1216,6 +1216,7 @@ static void encode_histogram_remap(FFV1Context *f, FFV1SliceContext *sc) } if (run) put_symbol(&sc->c, state[lu], run, 0); + sc->remap_count[p] = j; } } @@ -1370,7 +1371,8 @@ static int encode_float32_remap_segment(FFV1SliceContext *sc, mul[ current_mul_index ] *= -1; put_symbol_inline(&rc, state[0][2], mul[ current_mul_index ], 0, NULL, NULL); } - compact_index ++; + if (i < pixel_num) + compact_index ++; } } if (!run || run1final) @@ -1380,6 +1382,8 @@ static int encode_float32_remap_segment(FFV1SliceContext *sc, if (update) { sc->c = rc; + av_assert0(compact_index <= 65535); + sc->remap_count[p] = compact_index + 1; } return get_rac_count(&rc); } @@ -1492,10 +1496,11 @@ static int encode_float32_rgb_frame(FFV1Context *f, FFV1SliceContext *sc, const int ring_size = f->context_model ? 3 : 2; int32_t *sample[4][3]; const int pass1 = !!(f->avctx->flags & AV_CODEC_FLAG_PASS1); - int bits = 16; //TODO explain this in the specifciation, we have 32bits in but really encode max 16 - int offset = 1 << bits; + int bits[4], offset; int transparency = f->transparency; + ff_ffv1_compute_bits_per_plane(f, sc, bits, &offset, NULL, f->bits_per_raw_sample); + sc->run_index = 0; memset(RENAME(sc->sample_buffer), 0, ring_size * MAX_PLANES * @@ -1532,7 +1537,7 @@ static int encode_float32_rgb_frame(FFV1Context *f, FFV1SliceContext *sc, sample[p][0][-1] = sample[p][1][0 ]; sample[p][1][ w] = sample[p][1][w-1]; ret = encode_line32(f, sc, f->avctx, w, sample[p], (p + 1) / 2, - bits + (sc->slice_coding_mode != 1), ac, pass1); + bits[p], ac, pass1); if (ret < 0) return ret; } diff --git a/libavcodec/ffv1enc_template.c b/libavcodec/ffv1enc_template.c index 85d09da99a5..64f3c420c51 100644 --- a/libavcodec/ffv1enc_template.c +++ b/libavcodec/ffv1enc_template.c @@ -35,6 +35,9 @@ RENAME(encode_line)(FFV1Context *f, FFV1SliceContext *sc, int run_count = 0; int run_mode = 0; + if (bits == 0) + return 0; + if (ac != AC_GOLOMB_RICE) { if (c->bytestream_end - c->bytestream < w * 35) { av_log(logctx, AV_LOG_ERROR, "encoded Range Coder frame too large\n"); @@ -172,11 +175,12 @@ static int RENAME(encode_rgb_frame)(FFV1Context *f, FFV1SliceContext *sc, const int pass1 = !!(f->avctx->flags & AV_CODEC_FLAG_PASS1); int lbd = f->bits_per_raw_sample <= 8; int packed = !src[1]; - int bits = f->bits_per_raw_sample > 0 ? f->bits_per_raw_sample : 8; - int offset = 1 << bits; + int bits[4], offset; int transparency = f->transparency; int packed_size = (3 + transparency)*2; + ff_ffv1_compute_bits_per_plane(f, sc, bits, &offset, NULL, f->bits_per_raw_sample); + sc->run_index = 0; memset(RENAME(sc->sample_buffer), 0, ring_size * MAX_PLANES * @@ -239,11 +243,11 @@ static int RENAME(encode_rgb_frame)(FFV1Context *f, FFV1SliceContext *sc, int ret; sample[p][0][-1] = sample[p][1][0 ]; sample[p][1][ w] = sample[p][1][w-1]; - if (lbd && sc->slice_coding_mode == 0) + if (bits[p] == 9) ret = RENAME(encode_line)(f, sc, f->avctx, w, sample[p], (p + 1) / 2, 9, ac, pass1); else ret = RENAME(encode_line)(f, sc, f->avctx, w, sample[p], (p + 1) / 2, - bits + (sc->slice_coding_mode != 1), ac, pass1); + bits[p], ac, pass1); if (ret < 0) return ret; } -- 2.49.0 _______________________________________________ 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-04-03 0:36 UTC|newest] Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top 2025-04-03 0:36 Michael Niedermayer [this message] 2025-04-06 18:40 ` 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=20250403003636.106191-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