On 2/6/20, Robert Deibel <deibel.rob...@googlemail.com> wrote: > Would appreciate further review or info.
Are you sure this change does not break older scripts? > > On 29.01.20 10:54, Robert Deibel wrote: >> Fix shaking of image when zoom is applied by the zoompan filter. >> Resolves ticket https://trac.ffmpeg.org/ticket/4298 >> --- >> Fixed a typo/bug where dy was dx. Removed request for oversized frame from >> filter, instead allocate >> internal frame and copy contents to out frame once dimensions are correct. >> >> libavfilter/vf_zoompan.c | 109 +++++++++++++++++++++++++++++---------- >> 1 file changed, 82 insertions(+), 27 deletions(-) >> >> diff --git a/libavfilter/vf_zoompan.c b/libavfilter/vf_zoompan.c >> index 59c9b19ec8..e18f9c8d1b 100644 >> --- a/libavfilter/vf_zoompan.c >> +++ b/libavfilter/vf_zoompan.c >> @@ -150,16 +150,30 @@ static int config_output(AVFilterLink *outlink) >> return 0; >> } >> >> +/** >> + * Scales n to the next number divisible by 2 * 2^log_grid_size but >> minimally n + 2 * 2^log_grid_size. >> + * >> + * Used to scale the width and height of a frame to fit with the >> subsampling grid. >> + * @param n The number to be scaled. >> + * @param log_grid_size log 2 of the gridsize. >> + * @return The next number divisible by 2 * 2^log_grid_size but minimally >> n + 2 * 2^log_grid_size >> + */ >> +static int scale_to_grid(int n, uint8_t log2_grid_size) >> +{ >> + return (((n + (1 << log2_grid_size) * 2) & ~((1 << log2_grid_size) - >> 1)) + 1) & ~1;; >> +} >> + >> static int output_single_frame(AVFilterContext *ctx, AVFrame *in, double >> *var_values, int i, >> double *zoom, double *dx, double *dy) >> { >> ZPContext *s = ctx->priv; >> AVFilterLink *outlink = ctx->outputs[0]; >> int64_t pts = s->frame_count; >> - int k, x, y, w, h, ret = 0; >> + int k, x, y, crop_x, crop_y, w, h, crop_w, crop_h, overscaled_w, >> overscaled_h, ret = 0; >> uint8_t *input[4]; >> int px[4], py[4]; >> - AVFrame *out; >> + AVFrame *out, *overscaled_frame; >> + double dw, dh, dx_scaled, dy_scaled; >> >> var_values[VAR_PX] = s->x; >> var_values[VAR_PY] = s->y; >> @@ -173,32 +187,44 @@ static int output_single_frame(AVFilterContext *ctx, >> AVFrame *in, double *var_va >> >> *zoom = av_clipd(*zoom, 1, 10); >> var_values[VAR_ZOOM] = *zoom; >> - w = in->width * (1.0 / *zoom); >> - h = in->height * (1.0 / *zoom); >> >> - *dx = av_expr_eval(s->x_expr, var_values, NULL); >> + w = dw = (double) in->width / *zoom; >> + h = dh = (double) in->height / *zoom; >> + crop_w = scale_to_grid(w, s->desc->log2_chroma_w); >> + crop_h = scale_to_grid(h, s->desc->log2_chroma_h); >> + overscaled_w = outlink->w + (crop_w - dw) * *zoom; >> + overscaled_h = outlink->h + (crop_h - dh) * *zoom; >> >> - x = *dx = av_clipd(*dx, 0, FFMAX(in->width - w, 0)); >> - var_values[VAR_X] = *dx; >> - x &= ~((1 << s->desc->log2_chroma_w) - 1); >> + *dx = av_expr_eval(s->x_expr, var_values, NULL); >> + crop_x = ceil(av_clipd(*dx - (((double) crop_w - w) / 2.0), 0, >> FFMAX(in->width - crop_w, 0))); >> + var_values[VAR_X] = *dx = av_clipd(*dx, 0, FFMAX(in->width - w, 0)); >> + crop_x &= ~((1 << s->desc->log2_chroma_w) - 1); >> >> *dy = av_expr_eval(s->y_expr, var_values, NULL); >> + crop_y = ceil(av_clipd(*dy - (((double) crop_h - h)/ 2.0), 0, >> FFMAX(in->height - crop_h, 0))); >> + var_values[VAR_Y] = *dy = av_clipd(*dy, 0, FFMAX(in->height - h, 0)); >> + crop_y &= ~((1 << s->desc->log2_chroma_h) - 1); >> >> - y = *dy = av_clipd(*dy, 0, FFMAX(in->height - h, 0)); >> - var_values[VAR_Y] = *dy; >> - y &= ~((1 << s->desc->log2_chroma_h) - 1); >> - >> - out = ff_get_video_buffer(outlink, outlink->w, outlink->h); >> - if (!out) { >> + overscaled_frame = av_frame_alloc(); >> + if (!overscaled_frame) { >> ret = AVERROR(ENOMEM); >> return ret; >> } >> >> - px[1] = px[2] = AV_CEIL_RSHIFT(x, s->desc->log2_chroma_w); >> - px[0] = px[3] = x; >> + overscaled_frame->height = overscaled_h; >> + overscaled_frame->width = overscaled_w; >> + overscaled_frame->format = outlink->format; >> + if ((ret = av_frame_get_buffer(overscaled_frame, 0)) < 0) >> + goto error; >> >> - py[1] = py[2] = AV_CEIL_RSHIFT(y, s->desc->log2_chroma_h); >> - py[0] = py[3] = y; >> + px[1] = px[2] = AV_CEIL_RSHIFT(crop_x, s->desc->log2_chroma_w); >> + px[0] = px[3] = crop_x; >> + >> + py[1] = py[2] = AV_CEIL_RSHIFT(crop_y, s->desc->log2_chroma_h); >> + py[0] = py[3] = crop_y; >> + >> + for (k = 0; k < 4; k++) >> + input[k] = in->data[k] + py[k] * in->linesize[k] + px[k]; >> >> s->sws = sws_alloc_context(); >> if (!s->sws) { >> @@ -206,21 +232,48 @@ static int output_single_frame(AVFilterContext *ctx, >> AVFrame *in, double *var_va >> goto error; >> } >> >> - for (k = 0; in->data[k]; k++) >> - input[k] = in->data[k] + py[k] * in->linesize[k] + px[k]; >> - >> - av_opt_set_int(s->sws, "srcw", w, 0); >> - av_opt_set_int(s->sws, "srch", h, 0); >> + av_opt_set_int(s->sws, "srcw", crop_w, 0); >> + av_opt_set_int(s->sws, "srch", crop_h, 0); >> av_opt_set_int(s->sws, "src_format", in->format, 0); >> - av_opt_set_int(s->sws, "dstw", outlink->w, 0); >> - av_opt_set_int(s->sws, "dsth", outlink->h, 0); >> + av_opt_set_int(s->sws, "dstw", overscaled_w, 0); >> + av_opt_set_int(s->sws, "dsth", overscaled_h, 0); >> av_opt_set_int(s->sws, "dst_format", outlink->format, 0); >> av_opt_set_int(s->sws, "sws_flags", SWS_BICUBIC, 0); >> >> if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0) >> goto error; >> >> - sws_scale(s->sws, (const uint8_t *const *)&input, in->linesize, 0, h, >> out->data, out->linesize); >> + sws_scale(s->sws, (const uint8_t *const *)&input, in->linesize, 0, >> crop_h, overscaled_frame->data, overscaled_frame->linesize); >> + >> + dx_scaled = ((crop_w - dw) * *zoom) / (((double) crop_w - dw) / (*dx >> - (double) crop_x)); >> + x = ceil(av_clipd(dx_scaled, 0, FFMAX(overscaled_w - outlink->w, >> 0))); >> + x &= ~((1 << s->desc->log2_chroma_w) - 1); >> + >> + dy_scaled = ((crop_h - dh) * *zoom) / (((double) crop_h - dh) / (*dy >> - (double) crop_y)); >> + y = ceil(av_clipd(dy_scaled, 0, FFMAX(overscaled_h - outlink->h, >> 0))); >> + y &= ~((1 << s->desc->log2_chroma_h) - 1); >> + >> + px[1] = px[2] = AV_CEIL_RSHIFT(x, s->desc->log2_chroma_w); >> + px[0] = px[3] = x; >> + >> + py[1] = py[2] = AV_CEIL_RSHIFT(y, s->desc->log2_chroma_h); >> + py[0] = py[3] = y; >> + >> + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); >> + if (!out) { >> + ret = AVERROR(ENOMEM); >> + goto error; >> + } >> + >> + overscaled_frame->crop_left = x; >> + overscaled_frame->crop_right = overscaled_w - outlink->w - x; >> + overscaled_frame->crop_top = y; >> + overscaled_frame->crop_bottom = overscaled_h - outlink->h - y; >> + if ((ret = av_frame_apply_cropping(overscaled_frame, >> AV_FRAME_CROP_UNALIGNED)) < 0) >> + goto error; >> + >> + if ((ret = av_frame_copy(out, overscaled_frame)) < 0) >> + goto error; >> >> out->pts = pts; >> s->frame_count++; >> @@ -229,6 +282,7 @@ static int output_single_frame(AVFilterContext *ctx, >> AVFrame *in, double *var_va >> sws_freeContext(s->sws); >> s->sws = NULL; >> s->current_frame++; >> + av_frame_free(&overscaled_frame); >> >> if (s->current_frame >= s->nb_frames) { >> if (*dx != -1) >> @@ -245,9 +299,10 @@ static int output_single_frame(AVFilterContext *ctx, >> AVFrame *in, double *var_va >> } >> return ret; >> error: >> + av_frame_free(&out); >> sws_freeContext(s->sws); >> s->sws = NULL; >> - av_frame_free(&out); >> + av_frame_free(&overscaled_frame); >> return ret; >> } >> > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe". _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".