diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2013-12-08 22:09:50 +0100 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2013-12-09 00:37:38 +0100 |
commit | 7854d2d2515dc2a54c5f309100aeecf83cd14e97 (patch) | |
tree | 5c5b403b90264c20b117e321e9cbaf390b263f3b | |
parent | 1b264607883e7d52a3941cd9c192e3045096acc9 (diff) | |
download | ffmpeg-7854d2d2515dc2a54c5f309100aeecf83cd14e97.tar.gz |
avcodec/ffv1: support adjusting the g vs r + b coefficient in the RCT
about 1% better compression
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r-- | libavcodec/ffv1.h | 1 | ||||
-rw-r--r-- | libavcodec/ffv1dec.c | 11 | ||||
-rw-r--r-- | libavcodec/ffv1enc.c | 79 |
3 files changed, 85 insertions, 6 deletions
diff --git a/libavcodec/ffv1.h b/libavcodec/ffv1.h index bdc7b862d0..2c3e6e4c98 100644 --- a/libavcodec/ffv1.h +++ b/libavcodec/ffv1.h @@ -130,6 +130,7 @@ typedef struct FFV1Context { int slice_y; int slice_reset_contexts; int slice_coding_mode; + int slice_rct_y_coef; } FFV1Context; int ffv1_common_init(AVCodecContext *avctx); diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c index 166bc259f0..6c01ebf15f 100644 --- a/libavcodec/ffv1dec.c +++ b/libavcodec/ffv1dec.c @@ -260,7 +260,7 @@ static void decode_rgb_frame(FFV1Context *s, uint8_t *src[3], int w, int h, int if (s->slice_coding_mode != 1) { b -= offset; r -= offset; - g -= (b + r) >> 2; + g -= ((b + r) * s->slice_rct_y_coef) >> 2; b += g; r += g; } @@ -333,6 +333,13 @@ static int decode_slice_header(FFV1Context *f, FFV1Context *fs) if (fs->version > 3) { fs->slice_reset_contexts = get_rac(c, state); fs->slice_coding_mode = get_symbol(c, state, 0); + if (fs->slice_coding_mode != 1) { + fs->slice_rct_y_coef = get_symbol(c, state, 0); + if (fs->slice_rct_y_coef > 2U) { + av_log(f->avctx, AV_LOG_ERROR, "slice_rct_y_coef out of range\n"); + return AVERROR_INVALIDDATA; + } + } } return 0; } @@ -381,6 +388,8 @@ static int decode_slice(AVCodecContext *c, void *arg) } } + fs->slice_rct_y_coef = 1; + if (f->version > 2) { if (ffv1_init_slice_state(f, fs) < 0) return AVERROR(ENOMEM); diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c index 2c87d7ae86..4e2e0b3da9 100644 --- a/libavcodec/ffv1enc.c +++ b/libavcodec/ffv1enc.c @@ -441,7 +441,7 @@ static int encode_rgb_frame(FFV1Context *s, uint8_t *src[3], int w, int h, int s if (s->slice_coding_mode != 1) { b -= g; r -= g; - g += (b + r) >> 2; + g += ((b + r) * s->slice_rct_y_coef) >> 2; b += offset; r += offset; } @@ -557,8 +557,10 @@ static int write_extradata(FFV1Context *f) put_symbol(c, state, f->version, 0); if (f->version > 2) { - if (f->version == 3) + if (f->version == 3) { f->micro_version = 4; + } else if (f->version == 4) + f->micro_version = 1; put_symbol(c, state, f->micro_version, 0); } @@ -995,9 +997,71 @@ static void encode_slice_header(FFV1Context *f, FFV1Context *fs) if (fs->slice_coding_mode == 1) ffv1_clear_slice_state(f, fs); put_symbol(c, state, fs->slice_coding_mode, 0); + if (fs->slice_coding_mode != 1) + put_symbol(c, state, fs->slice_rct_y_coef, 0); } } +static void choose_rct_params(FFV1Context *fs, uint8_t *src[3], const int stride[3], int w, int h) +{ + int stat[3] = {0}; + int x, y, i, p, best; + int16_t *sample[3]; + int lbd = fs->bits_per_raw_sample <= 8; + + for (y = 0; y < h; y++) { + int lastr=0, lastg=0, lastb=0; + for (p = 0; p < 3; p++) + sample[p] = fs->sample_buffer + p*w; + + for (x = 0; x < w; x++) { + int b, g, r; + if (lbd) { + unsigned v = *((uint32_t*)(src[0] + x*4 + stride[0]*y)); + b = v & 0xFF; + g = (v >> 8) & 0xFF; + r = (v >> 16) & 0xFF; + } else { + b = *((uint16_t*)(src[0] + x*2 + stride[0]*y)); + g = *((uint16_t*)(src[1] + x*2 + stride[1]*y)); + r = *((uint16_t*)(src[2] + x*2 + stride[2]*y)); + } + + if (x && y) { + int ar = r - lastr; + int ag = g - lastg; + int ab = b - lastb; + int bg = ag - sample[0][x]; + int bb = ab - sample[1][x]; + int br = ar - sample[2][x]; + + br -= bg; + bb -= bg; + + stat[0] += FFABS(bg); + stat[1] += FFABS(bg + ((br+bb)>>2)); + stat[2] += FFABS(bg + ((br+bb)>>1)); + + sample[0][x] = ag; + sample[1][x] = ab; + sample[2][x] = ar; + } + + lastr = r; + lastg = g; + lastb = b; + } + } + + best = 0; + for (i=1; i<=2; i++) { + if (stat[i] < stat[best]) + best = i; + } + + fs->slice_rct_y_coef = best; +} + static int encode_slice(AVCodecContext *c, void *arg) { FFV1Context *fs = *(void **)arg; @@ -1010,8 +1074,16 @@ static int encode_slice(AVCodecContext *c, void *arg) const int ps = av_pix_fmt_desc_get(c->pix_fmt)->comp[0].step_minus1 + 1; int ret; RangeCoder c_bak = fs->c; + uint8_t *planes[3] = {p->data[0] + ps*x + y*p->linesize[0], + p->data[1] + ps*x + y*p->linesize[1], + p->data[2] + ps*x + y*p->linesize[2]}; fs->slice_coding_mode = 0; + if (f->version > 3) { + choose_rct_params(fs, planes, p->linesize, width, height); + } else { + fs->slice_rct_y_coef = 1; + } retry: if (c->coded_frame->key_frame) @@ -1043,9 +1115,6 @@ retry: if (fs->transparency) ret |= encode_plane(fs, p->data[3] + ps*x + y*p->linesize[3], width, height, p->linesize[3], 2); } else { - uint8_t *planes[3] = {p->data[0] + ps*x + y*p->linesize[0], - p->data[1] + ps*x + y*p->linesize[1], - p->data[2] + ps*x + y*p->linesize[2]}; ret = encode_rgb_frame(fs, planes, width, height, p->linesize); } emms_c(); |