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>
This commit is contained in:
Michael Niedermayer 2013-12-08 22:09:50 +01:00
parent 1b26460788
commit 7854d2d251
3 changed files with 85 additions and 6 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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();