From patchwork Wed Jan 29 09:54:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Deibel X-Patchwork-Id: 17609 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 1806044B95F for ; Wed, 29 Jan 2020 11:55:39 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EA186689E14; Wed, 29 Jan 2020 11:55:38 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm1-f68.google.com (mail-wm1-f68.google.com [209.85.128.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 57C85688127 for ; Wed, 29 Jan 2020 11:55:32 +0200 (EET) Received: by mail-wm1-f68.google.com with SMTP id a9so5699878wmj.3 for ; Wed, 29 Jan 2020 01:55:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=15MAdBc15XEewwiq6C+YaLtrMzDelhwKfjnrld/Ylvk=; b=G4VFiZiin2YXCyac+QttqIbSA4cPjIlY8ct5spE7xzJCtHnehyoxsix7Fmu7svMrBZ rn6TqUY9Vy8sgwm+9rS3pAyHHadrg/I16sIiI8/FUQKvykm9RmGZjpc2WeE8uf26wUdT G5eDhz2Jjmhvh9vqMUkfP4YhL6p1e/U8+T4bR/R/FFRbsJr0RMZtm+ZIOR1x8U/tBssy mU0worz3qA0Bc8NoW04vrygj2em6brNgWLSeG7UCaVIPiLnb78C6KwQaQKiuD41Ndr3J 8QzHRgs4n8HmEHYkOMnnWjNVfxNfSeackP5l4hHuewYC6EH8k9ww5wnh5YsYg4KN0qV/ yJHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=15MAdBc15XEewwiq6C+YaLtrMzDelhwKfjnrld/Ylvk=; b=k15y+ei4VmMcUzhfAfUhEuyjgGYvo4FGK6lz3T/a2qHppv+iGLA6hbIqUPh8sIH48g PPqN5bw0EpjUFDC3Im/in/qXoPIVNeW9D8ogC+vVq/Bw11m02hcdQu+vnZ3tU5tG3bjc dSGckIroPr61Slf1n/iEtfrtgoxwsXlaDf70R/ECA5EDjGTImLujxa0Sk6eQB9MbQgNa OGtxqNLyAE68PmU13hbOOBLlOaCyMdj/vcFdtQbFCDfxzt9gagLuzkYdJQzZl/HgSBIB wwDCG1+Bzw6XwRylx5G8LRMiQvAHuzibt4Bjg06gCVBgUqSKwm5JY+RVzXWIqVdUd6xQ gQeg== X-Gm-Message-State: APjAAAWezAgkxcZuEh6+GlW8xx+5Lwf/INOwuNMt/DzrHt9+uzTZsHBi GRPQkHZXQbiofety2IwBZ82J3JR9JME= X-Google-Smtp-Source: APXvYqzAsMX9XYu0K4jnCCwZNQCqCr9SxHeaKHgdsJJmrtjD0o1W5g/1mwOBd85057BcmwxwuoIJuw== X-Received: by 2002:a1c:5419:: with SMTP id i25mr10699282wmb.150.1580291731260; Wed, 29 Jan 2020 01:55:31 -0800 (PST) Received: from lars.fritz.box (b2b-37-24-22-101.unitymedia.biz. [37.24.22.101]) by smtp.gmail.com with ESMTPSA id z21sm1672439wml.5.2020.01.29.01.55.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Jan 2020 01:55:30 -0800 (PST) From: Robert Deibel To: ffmpeg-devel@ffmpeg.org Date: Wed, 29 Jan 2020 10:54:51 +0100 Message-Id: <20200129095451.14747-1-deibel.robert@googlemail.com> X-Mailer: git-send-email 2.25.0 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v4] avfilter/vf_zoompan: fix shaking when zooming X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Robert Deibel Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" 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; }