PR #22528 opened by Lynne URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22528 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22528.patch
This series of commits implements FFv1 Bayer encoding and decoding. RGB encoding was used as a basis. A 2x2 Bayer RCT was implemented, based on "Reversible color transform for Bayer color filter array images", which decorrelates the 2 green elements in each Bayer quad into a median and a difference. The 2 elements share the same probability context, but are predicted differently. This is similar to the partial debayering process that Blackmagic and other codecs ""RAW"" codecs do to encode Bayered sensor data, except we maintain losslessness. >From 8a7f71ef1b2c44ea32edc1cf44bbcdf636b9a8d5 Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Tue, 17 Mar 2026 13:37:39 +0100 Subject: [PATCH 1/2] ffv1enc: implement Bayer pixel format encoding Sponsored-by: Sovereign Tech Fund --- libavcodec/ffv1.h | 1 + libavcodec/ffv1enc.c | 92 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/libavcodec/ffv1.h b/libavcodec/ffv1.h index 8a48e8e682..388d91e17b 100644 --- a/libavcodec/ffv1.h +++ b/libavcodec/ffv1.h @@ -153,6 +153,7 @@ typedef struct FFV1Context { int flt; int remap_mode; int remap_optimizer; + int bayer; int maxsize_warned; int use32bit; diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c index b6d25013f0..115f3d48bf 100644 --- a/libavcodec/ffv1enc.c +++ b/libavcodec/ffv1enc.c @@ -435,7 +435,7 @@ static void set_micro_version(FFV1Context *f) if (f->version == 3) { f->micro_version = 4; } else if (f->version == 4) { - f->micro_version = 9; + f->micro_version = 10; } else av_assert0(0); @@ -480,6 +480,8 @@ av_cold int ff_ffv1_write_extradata(AVCodecContext *avctx) put_symbol(&c, state, f->chroma_h_shift, 0); put_symbol(&c, state, f->chroma_v_shift, 0); put_rac(&c, state, f->transparency); + if (f->version >= 4 && f->micro_version >= 10) + put_rac(&c, state, f->bayer); put_symbol(&c, state, f->num_h_slices - 1, 0); put_symbol(&c, state, f->num_v_slices - 1, 0); @@ -566,7 +568,7 @@ static int sort_stt(FFV1Context *s, uint8_t stt[256]) int ff_ffv1_encode_determine_slices(AVCodecContext *avctx) { FFV1Context *s = avctx->priv_data; - int plane_count = 1 + 2*s->chroma_planes + s->transparency; + int plane_count = 1 + 2*s->chroma_planes + s->bayer + s->transparency; int max_h_slices = AV_CEIL_RSHIFT(avctx->width , s->chroma_h_shift); int max_v_slices = AV_CEIL_RSHIFT(avctx->height, s->chroma_v_shift); s->num_v_slices = (avctx->width > 352 || avctx->height > 288 || !avctx->slices) ? 2 : 1; @@ -804,6 +806,7 @@ av_cold int ff_ffv1_encode_setup_plane_info(AVCodecContext *avctx, FFV1Context *s = avctx->priv_data; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); + s->bayer = 0; s->plane_count = 3; switch(pix_fmt) { case AV_PIX_FMT_GRAY9: @@ -906,6 +909,14 @@ av_cold int ff_ffv1_encode_setup_plane_info(AVCodecContext *avctx, s->use32bit = 1; s->version = FFMAX(s->version, 1); break; + case AV_PIX_FMT_BAYER_RGGB16: + s->colorspace = 1; + s->chroma_planes = 1; + s->bits_per_raw_sample = 16; + s->use32bit = 1; + s->version = FFMAX(s->version, 4); + s->bayer = 1; + break; case AV_PIX_FMT_GBRP: case AV_PIX_FMT_0RGB32: s->colorspace = 1; @@ -1559,6 +1570,77 @@ static int encode_float32_rgb_frame(FFV1Context *f, FFV1SliceContext *sc, return 0; } +static int encode_bayer_frame(FFV1Context *f, FFV1SliceContext *sc, + const uint8_t *src[4], + int w, int h, const int stride[4], int ac) +{ + const int pass1 = !!(f->avctx->flags & AV_CODEC_FLAG_PASS1); + const int ring_size = f->context_model ? 3 : 2; + TYPE *sample[4][3]; + + int bits[4], offset; + ff_ffv1_compute_bits_per_plane(f, sc, bits, &offset, NULL, f->bits_per_raw_sample); + + sc->run_index = 0; + + for (int p = 0; p < MAX_PLANES; ++p) + sample[p][2] = RENAME(sc->sample_buffer); + + memset(RENAME(sc->sample_buffer), 0, ring_size * MAX_PLANES * + (w + 6) * sizeof(*RENAME(sc->sample_buffer))); + + for (int y = 0; y < h; y += 2) { + for (int i = 0; i < ring_size; i++) + for (int p = 0; p < MAX_PLANES; p++) + sample[p][i] = RENAME(sc->sample_buffer) + p*ring_size*(w+6) + + ((h+i-y) % ring_size)*(w+6) + 3; + + for (int x = 0; x < w; x += 2) { + const uint16_t *l1 = ((const uint16_t*)(src[0] + stride[0]*(y + 0) + x*2)); + const uint16_t *l2 = ((const uint16_t*)(src[0] + stride[0]*(y + 1) + x*2)); + + int r, gr, gb, b; + r = l1[0]; + gr = l1[1]; + gb = l2[0]; + b = l2[1]; + + if (sc->slice_coding_mode != 1) { + /* Bayer 2x2 RCT, based on: + * "Reversible color transform for Bayer color filter + * array images" */ + int gd = gr - gb; + int gm = gr + (gd >> 1); + + b -= gm; + r -= gm; + gm += (b * sc->slice_rct_by_coef + r * sc->slice_rct_ry_coef) >> 2; + b += offset; + r += offset; + + gr = gm; + gb = gd; + } + + sample[1][0][x] = gr; + sample[2][0][x] = gb; + sample[3][0][x] = b; + sample[0][0][x] = r; + } + + for (int p = 0; p < 4; p++) { + int ret; + sample[p][0][-1] = sample[p][1][0 ]; + sample[p][1][ w] = sample[p][1][w-1]; + ret = RENAME(encode_line)(f, sc, f->avctx, w, sample[p], p > 1, + bits[p], ac, pass1); + if (ret < 0) + return ret; + } + } + + return 0; +} static int encode_slice(AVCodecContext *c, void *arg) { @@ -1581,7 +1663,7 @@ static int encode_slice(AVCodecContext *c, void *arg) int ac = f->ac; sc->slice_coding_mode = 0; - if (f->version > 3 && f->colorspace == 1) { + if (f->version > 3 && f->colorspace == 1 && !f->bayer) { choose_rct_params(f, sc, planes, p->linesize, width, height); } else { sc->slice_rct_by_coef = 1; @@ -1654,6 +1736,8 @@ retry: ret |= encode_plane(f, sc, p->data[0] + (ps>>1) + ps*x + y*p->linesize[0], width, height, p->linesize[0], 1, 1, 2, ac); } else if (f->bits_per_raw_sample == 32) { ret = encode_float32_rgb_frame(f, sc, planes, width, height, p->linesize, ac); + } else if (f->bayer) { + ret = encode_bayer_frame(f, sc, planes, width, height, p->linesize, ac); } else if (f->use32bit) { ret = encode_rgb_frame32(f, sc, planes, width, height, p->linesize, ac); } else { @@ -1696,7 +1780,7 @@ size_t ff_ffv1_encode_buffer_size(AVCodecContext *avctx) if (f->version > 3) { maxsize *= f->bits_per_raw_sample + 1; if (f->remap_mode) - maxsize += f->slice_count * 70000 * (1 + 2*f->chroma_planes + f->transparency); + maxsize += f->slice_count * 70000 * (1 + 2*f->chroma_planes + f->bayer + f->transparency); } else { maxsize += f->slice_count * 2 * (avctx->width + avctx->height); //for bug with slices that code some pixels more than once maxsize *= 8*(2*f->bits_per_raw_sample + 5); -- 2.52.0 >From 130d7be449d3ee5ea46707fd20872807eb98f310 Mon Sep 17 00:00:00 2001 From: Lynne <[email protected]> Date: Tue, 17 Mar 2026 14:05:51 +0100 Subject: [PATCH 2/2] ffv1dec: implement Bayer pixel format encoding Sponsored-by: Sovereign Tech Fund --- libavcodec/ffv1_parse.c | 7 +++++ libavcodec/ffv1dec.c | 70 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/libavcodec/ffv1_parse.c b/libavcodec/ffv1_parse.c index 10f3652ff5..2c74ec5dee 100644 --- a/libavcodec/ffv1_parse.c +++ b/libavcodec/ffv1_parse.c @@ -117,6 +117,8 @@ int ff_ffv1_read_extra_header(FFV1Context *f) f->chroma_h_shift = ff_ffv1_get_symbol(&c, state, 0); f->chroma_v_shift = ff_ffv1_get_symbol(&c, state, 0); f->transparency = get_rac(&c, state); + if (f->version >= 4 && f->micro_version >= 10) + f->bayer = get_rac(&c, state); f->plane_count = 1 + (f->chroma_planes || f->version<4) + f->transparency; f->num_h_slices = 1 + ff_ffv1_get_symbol(&c, state, 0); f->num_v_slices = 1 + ff_ffv1_get_symbol(&c, state, 0); @@ -429,6 +431,11 @@ int ff_ffv1_parse_header(FFV1Context *f, RangeCoder *c, uint8_t *state) f->pix_fmt = AV_PIX_FMT_GBRAPF32; } f->use32bit = 1; + } else if (f->avctx->bits_per_raw_sample == 16 && !f->transparency && + f->bayer) { + f->pix_fmt = AV_PIX_FMT_BAYER_RGGB16; + f->use32bit = 1; + f->bayer = 1; } } else { av_log(f->avctx, AV_LOG_ERROR, "colorspace not supported\n"); diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c index 28e6ec3c4d..1f5ca02c24 100644 --- a/libavcodec/ffv1dec.c +++ b/libavcodec/ffv1dec.c @@ -357,6 +357,73 @@ static int decode_remap(FFV1Context *f, FFV1SliceContext *sc) return 0; } +static int decode_bayer_frame(FFV1Context *f, FFV1SliceContext *sc, + GetBitContext *gb, + uint8_t *src, int w, int h, int stride) +{ + int x, y, p; + TYPE *sample[4][2]; + int ac = f->ac; + unsigned mask[4]; + + int bits[4], offset; + 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; + + for (x = 0; x < 4; x++) { + sample[x][0] = RENAME(sc->sample_buffer) + x * 2 * (w + 6) + 3; + sample[x][1] = RENAME(sc->sample_buffer) + (x * 2 + 1) * (w + 6) + 3; + } + + sc->run_index = 0; + + memset(RENAME(sc->sample_buffer), 0, 8 * (w + 6) * sizeof(*RENAME(sc->sample_buffer))); + + for (y = 0; y < h; y += 2) { + for (p = 0; p < 4; p++) { + int ret; + TYPE *temp = sample[p][0]; // FIXME: try a normal buffer + + sample[p][0] = sample[p][1]; + sample[p][1] = temp; + + sample[p][1][-1]= sample[p][0][0 ]; + sample[p][0][ w]= sample[p][0][w-1]; + ret = RENAME(decode_line)(f, sc, gb, w, sample[p], p > 1, bits[p], ac); + if (ret < 0) + return ret; + } + + for (x = 0; x < w; x += 2) { + int g_r = sample[0][1][x]; + int g_b = sample[1][1][x]; + int b = sample[2][1][x]; + int r = sample[3][1][x]; + + if (sc->slice_coding_mode != 1) { + b -= offset; + r -= offset; + g_r -= (b * sc->slice_rct_by_coef + r * sc->slice_rct_ry_coef) >> 2; + b += g_r; + r += g_r; + + /* Correlate green */ + int gd = g_b; + g_b = g_r - (gd >> 1); + g_r = g_r + gd; + } + + *((uint16_t*)(src + (x + 0)*2 + stride*(y + 0))) = r; + *((uint16_t*)(src + (x + 1)*2 + stride*(y + 0))) = g_r; + *((uint16_t*)(src + (x + 0)*2 + stride*(y + 1))) = g_b; + *((uint16_t*)(src + (x + 1)*2 + stride*(y + 1))) = b; + } + } + return 0; +} + static int decode_slice(AVCodecContext *c, void *arg) { FFV1Context *f = c->priv_data; @@ -446,6 +513,9 @@ static int decode_slice(AVCodecContext *c, void *arg) } else if (f->colorspace == 0) { decode_plane(f, sc, &gb, p->data[0] + ps*x + y*p->linesize[0] , width, height, p->linesize[0], 0, 0, 2, ac); decode_plane(f, sc, &gb, p->data[0] + ps*x + y*p->linesize[0] + (ps>>1), width, height, p->linesize[0], 1, 1, 2, ac); + } else if (f->bayer) { + decode_bayer_frame(f, sc, &gb, p->data[0] + ps * x + y * p->linesize[0], + width, height, p->linesize[0]); } else if (f->use32bit) { uint8_t *planes[4] = { p->data[0] + ps * x + y * p->linesize[0], p->data[1] + ps * x + y * p->linesize[1], -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
