avcodec/error_resilience: Optimize motion recovery code by using blcok lists

This makes the code 7 times faster with the testcase from libfuzzer
and should reduce the amount of timeouts we hit in automated fuzzing.
(for example 438/fuzz-2-ffmpeg_VIDEO_AV_CODEC_ID_RV40_fuzzer)

The code is also faster with more realistic input though the difference
is small here as that is far from the worst cases the fuzzers pick out

Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/targets/ffmpeg
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
This commit is contained in:
Michael Niedermayer 2017-01-22 21:14:05 +01:00
parent f1214ad5d9
commit d9d9fd9446
3 changed files with 82 additions and 33 deletions

View File

@ -373,23 +373,39 @@ static void v_block_filter(ERContext *s, uint8_t *dst, int w, int h,
} }
} }
#define MV_FROZEN 8
#define MV_CHANGED 4
#define MV_UNCHANGED 2
#define MV_LISTED 1
static av_always_inline void add_blocklist(int (*blocklist)[2], int *blocklist_length, uint8_t *fixed, int mb_x, int mb_y, int mb_xy)
{
if (fixed[mb_xy])
return;
fixed[mb_xy] = MV_LISTED;
blocklist[ *blocklist_length ][0] = mb_x;
blocklist[(*blocklist_length)++][1] = mb_y;
}
static void guess_mv(ERContext *s) static void guess_mv(ERContext *s)
{ {
uint8_t *fixed = s->er_temp_buffer; int (*blocklist)[2], (*next_blocklist)[2];
#define MV_FROZEN 4 uint8_t *fixed;
#define MV_CHANGED 2
#define MV_UNCHANGED 1
const int mb_stride = s->mb_stride; const int mb_stride = s->mb_stride;
const int mb_width = s->mb_width; const int mb_width = s->mb_width;
int mb_height = s->mb_height; int mb_height = s->mb_height;
int i, depth, num_avail; int i, depth, num_avail;
int mb_x, mb_y, mot_step, mot_stride; int mb_x, mb_y, mot_step, mot_stride;
int blocklist_length, next_blocklist_length;
if (s->last_pic.f && s->last_pic.f->data[0]) if (s->last_pic.f && s->last_pic.f->data[0])
mb_height = FFMIN(mb_height, (s->last_pic.f->height+15)>>4); mb_height = FFMIN(mb_height, (s->last_pic.f->height+15)>>4);
if (s->next_pic.f && s->next_pic.f->data[0]) if (s->next_pic.f && s->next_pic.f->data[0])
mb_height = FFMIN(mb_height, (s->next_pic.f->height+15)>>4); mb_height = FFMIN(mb_height, (s->next_pic.f->height+15)>>4);
blocklist = s->er_temp_buffer;
next_blocklist = (s->er_temp_buffer + 2 * sizeof(int) * s->mb_stride * s->mb_height);
fixed = s->er_temp_buffer + 4 * sizeof(int) * s->mb_stride * s->mb_height;
set_mv_strides(s, &mot_step, &mot_stride); set_mv_strides(s, &mot_step, &mot_stride);
num_avail = 0; num_avail = 0;
@ -439,8 +455,22 @@ static void guess_mv(ERContext *s)
return; return;
} }
blocklist_length = 0;
for (mb_y = 0; mb_y < mb_height; mb_y++) {
for (mb_x = 0; mb_x < mb_width; mb_x++) {
const int mb_xy = mb_x + mb_y * mb_stride;
if (fixed[mb_xy] == MV_FROZEN) {
if (mb_x) add_blocklist(blocklist, &blocklist_length, fixed, mb_x - 1, mb_y, mb_xy - 1);
if (mb_y) add_blocklist(blocklist, &blocklist_length, fixed, mb_x, mb_y - 1, mb_xy - mb_stride);
if (mb_x+1 < mb_width) add_blocklist(blocklist, &blocklist_length, fixed, mb_x + 1, mb_y, mb_xy + 1);
if (mb_y+1 < mb_height) add_blocklist(blocklist, &blocklist_length, fixed, mb_x, mb_y + 1, mb_xy + mb_stride);
}
}
}
for (depth = 0; ; depth++) { for (depth = 0; ; depth++) {
int changed, pass, none_left; int changed, pass, none_left;
int blocklist_index;
none_left = 1; none_left = 1;
changed = 1; changed = 1;
@ -449,20 +479,24 @@ static void guess_mv(ERContext *s)
int score_sum = 0; int score_sum = 0;
changed = 0; changed = 0;
for (mb_y = 0; mb_y < mb_height; mb_y++) { for (blocklist_index = 0; blocklist_index < blocklist_length; blocklist_index++) {
for (mb_x = (mb_y ^ pass) & 1; mb_x < s->mb_width; mb_x+=2) { const int mb_x = blocklist[blocklist_index][0];
const int mb_xy = mb_x + mb_y * s->mb_stride; const int mb_y = blocklist[blocklist_index][1];
int mv_predictor[8][2]; const int mb_xy = mb_x + mb_y * mb_stride;
int ref[8]; int mv_predictor[8][2];
int pred_count; int ref[8];
int j; int pred_count;
int best_score; int j;
int best_pred; int best_score;
int mot_index; int best_pred;
int prev_x, prev_y, prev_ref; int mot_index;
int prev_x, prev_y, prev_ref;
if ((mb_x ^ mb_y ^ pass) & 1)
continue;
av_assert2(fixed[mb_xy] != MV_FROZEN);
if (fixed[mb_xy] == MV_FROZEN)
continue;
av_assert1(!IS_INTRA(s->cur_pic.mb_type[mb_xy])); av_assert1(!IS_INTRA(s->cur_pic.mb_type[mb_xy]));
av_assert1(s->last_pic.f && s->last_pic.f->data[0]); av_assert1(s->last_pic.f && s->last_pic.f->data[0]);
@ -476,8 +510,7 @@ static void guess_mv(ERContext *s)
if (mb_y + 1 < mb_height) if (mb_y + 1 < mb_height)
j |= fixed[mb_xy + mb_stride]; j |= fixed[mb_xy + mb_stride];
if (!(j & MV_FROZEN)) av_assert2(j & MV_FROZEN);
continue;
if (!(j & MV_CHANGED) && pass > 1) if (!(j & MV_CHANGED) && pass > 1)
continue; continue;
@ -486,7 +519,7 @@ static void guess_mv(ERContext *s)
pred_count = 0; pred_count = 0;
mot_index = (mb_x + mb_y * mot_stride) * mot_step; mot_index = (mb_x + mb_y * mot_stride) * mot_step;
if (mb_x > 0 && fixed[mb_xy - 1]) { if (mb_x > 0 && fixed[mb_xy - 1] > 1) {
mv_predictor[pred_count][0] = mv_predictor[pred_count][0] =
s->cur_pic.motion_val[0][mot_index - mot_step][0]; s->cur_pic.motion_val[0][mot_index - mot_step][0];
mv_predictor[pred_count][1] = mv_predictor[pred_count][1] =
@ -495,7 +528,7 @@ static void guess_mv(ERContext *s)
s->cur_pic.ref_index[0][4 * (mb_xy - 1)]; s->cur_pic.ref_index[0][4 * (mb_xy - 1)];
pred_count++; pred_count++;
} }
if (mb_x + 1 < mb_width && fixed[mb_xy + 1]) { if (mb_x + 1 < mb_width && fixed[mb_xy + 1] > 1) {
mv_predictor[pred_count][0] = mv_predictor[pred_count][0] =
s->cur_pic.motion_val[0][mot_index + mot_step][0]; s->cur_pic.motion_val[0][mot_index + mot_step][0];
mv_predictor[pred_count][1] = mv_predictor[pred_count][1] =
@ -504,7 +537,7 @@ static void guess_mv(ERContext *s)
s->cur_pic.ref_index[0][4 * (mb_xy + 1)]; s->cur_pic.ref_index[0][4 * (mb_xy + 1)];
pred_count++; pred_count++;
} }
if (mb_y > 0 && fixed[mb_xy - mb_stride]) { if (mb_y > 0 && fixed[mb_xy - mb_stride] > 1) {
mv_predictor[pred_count][0] = mv_predictor[pred_count][0] =
s->cur_pic.motion_val[0][mot_index - mot_stride * mot_step][0]; s->cur_pic.motion_val[0][mot_index - mot_stride * mot_step][0];
mv_predictor[pred_count][1] = mv_predictor[pred_count][1] =
@ -513,7 +546,7 @@ static void guess_mv(ERContext *s)
s->cur_pic.ref_index[0][4 * (mb_xy - s->mb_stride)]; s->cur_pic.ref_index[0][4 * (mb_xy - s->mb_stride)];
pred_count++; pred_count++;
} }
if (mb_y + 1<mb_height && fixed[mb_xy + mb_stride]) { if (mb_y + 1<mb_height && fixed[mb_xy + mb_stride] > 1) {
mv_predictor[pred_count][0] = mv_predictor[pred_count][0] =
s->cur_pic.motion_val[0][mot_index + mot_stride * mot_step][0]; s->cur_pic.motion_val[0][mot_index + mot_stride * mot_step][0];
mv_predictor[pred_count][1] = mv_predictor[pred_count][1] =
@ -606,24 +639,24 @@ skip_mean_and_median:
s->decode_mb(s->opaque, ref[j], MV_DIR_FORWARD, s->decode_mb(s->opaque, ref[j], MV_DIR_FORWARD,
MV_TYPE_16X16, &s->mv, mb_x, mb_y, 0, 0); MV_TYPE_16X16, &s->mv, mb_x, mb_y, 0, 0);
if (mb_x > 0 && fixed[mb_xy - 1]) { if (mb_x > 0 && fixed[mb_xy - 1] > 1) {
int k; int k;
for (k = 0; k < 16; k++) for (k = 0; k < 16; k++)
score += FFABS(src[k * linesize[0] - 1] - score += FFABS(src[k * linesize[0] - 1] -
src[k * linesize[0]]); src[k * linesize[0]]);
} }
if (mb_x + 1 < mb_width && fixed[mb_xy + 1]) { if (mb_x + 1 < mb_width && fixed[mb_xy + 1] > 1) {
int k; int k;
for (k = 0; k < 16; k++) for (k = 0; k < 16; k++)
score += FFABS(src[k * linesize[0] + 15] - score += FFABS(src[k * linesize[0] + 15] -
src[k * linesize[0] + 16]); src[k * linesize[0] + 16]);
} }
if (mb_y > 0 && fixed[mb_xy - mb_stride]) { if (mb_y > 0 && fixed[mb_xy - mb_stride] > 1) {
int k; int k;
for (k = 0; k < 16; k++) for (k = 0; k < 16; k++)
score += FFABS(src[k - linesize[0]] - src[k]); score += FFABS(src[k - linesize[0]] - src[k]);
} }
if (mb_y + 1 < mb_height && fixed[mb_xy + mb_stride]) { if (mb_y + 1 < mb_height && fixed[mb_xy + mb_stride] > 1) {
int k; int k;
for (k = 0; k < 16; k++) for (k = 0; k < 16; k++)
score += FFABS(src[k + linesize[0] * 15] - score += FFABS(src[k + linesize[0] * 15] -
@ -654,18 +687,34 @@ skip_mean_and_median:
changed++; changed++;
} else } else
fixed[mb_xy] = MV_UNCHANGED; fixed[mb_xy] = MV_UNCHANGED;
}
} }
} }
if (none_left) if (none_left)
return; return;
for (i = 0; i < mb_width * mb_height; i++) { next_blocklist_length = 0;
int mb_xy = s->mb_index2xy[i];
if (fixed[mb_xy]) for (blocklist_index = 0; blocklist_index < blocklist_length; blocklist_index++) {
const int mb_x = blocklist[blocklist_index][0];
const int mb_y = blocklist[blocklist_index][1];
const int mb_xy = mb_x + mb_y * mb_stride;
if (fixed[mb_xy] & (MV_CHANGED|MV_UNCHANGED|MV_FROZEN)) {
fixed[mb_xy] = MV_FROZEN; fixed[mb_xy] = MV_FROZEN;
if (mb_x > 0)
add_blocklist(next_blocklist, &next_blocklist_length, fixed, mb_x - 1, mb_y, mb_xy - 1);
if (mb_y > 0)
add_blocklist(next_blocklist, &next_blocklist_length, fixed, mb_x, mb_y - 1, mb_xy - mb_stride);
if (mb_x + 1 < mb_width)
add_blocklist(next_blocklist, &next_blocklist_length, fixed, mb_x + 1, mb_y, mb_xy + 1);
if (mb_y + 1 < mb_height)
add_blocklist(next_blocklist, &next_blocklist_length, fixed, mb_x, mb_y + 1, mb_xy + mb_stride);
}
} }
av_assert0(next_blocklist_length <= mb_height * mb_width);
FFSWAP(int , blocklist_length, next_blocklist_length);
FFSWAP(void*, blocklist, next_blocklist);
} }
} }

View File

@ -285,7 +285,7 @@ int ff_h264_slice_context_init(H264Context *h, H264SliceContext *sl)
mb_array_size * sizeof(uint8_t), fail); mb_array_size * sizeof(uint8_t), fail);
FF_ALLOC_OR_GOTO(h->avctx, er->er_temp_buffer, FF_ALLOC_OR_GOTO(h->avctx, er->er_temp_buffer,
h->mb_height * h->mb_stride, fail); h->mb_height * h->mb_stride * (4*sizeof(int) + 1), fail);
FF_ALLOCZ_OR_GOTO(h->avctx, sl->dc_val_base, FF_ALLOCZ_OR_GOTO(h->avctx, sl->dc_val_base,
yc_size * sizeof(int16_t), fail); yc_size * sizeof(int16_t), fail);

View File

@ -109,7 +109,7 @@ int ff_mpeg_er_init(MpegEncContext *s)
er->mb_stride = s->mb_stride; er->mb_stride = s->mb_stride;
er->b8_stride = s->b8_stride; er->b8_stride = s->b8_stride;
er->er_temp_buffer = av_malloc(s->mb_height * s->mb_stride); er->er_temp_buffer = av_malloc(s->mb_height * s->mb_stride * (4*sizeof(int) + 1));
er->error_status_table = av_mallocz(mb_array_size); er->error_status_table = av_mallocz(mb_array_size);
if (!er->er_temp_buffer || !er->error_status_table) if (!er->er_temp_buffer || !er->error_status_table)
goto fail; goto fail;